From 901a5f722049c6316d137688756613f06a57d423 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 Aug 2009 07:09:59 +0530 Subject: [PATCH 001/850] gl-add-auth-keys; first version, pretty much done --- gl-add-auth-keys | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100755 gl-add-auth-keys diff --git a/gl-add-auth-keys b/gl-add-auth-keys new file mode 100755 index 0000000..dfadc1a --- /dev/null +++ b/gl-add-auth-keys @@ -0,0 +1,71 @@ +#!/bin/bash + +# === add-auth-keys === +# refreshes ~/.ssh/authorized_keys from the list of pub-keys + +# part of the gitosis-lite (GL) suite + +# how run: manual, by GL admin +# when: anytime a pubkey is added/deleted +# (i.e., contents of ~/.gitosis-lite/pubkeys change) +# input: ~/.gitosis-lite/pubkeys +# output: ~/.ssh/authorized_keys +# security: +# - touches a very critical system file that manages the restrictions on +# incoming users. Be sure to audit AUTH_COMMAND and AUTH_OPTIONS (see +# below) on any change to this script +# - no security checks within program. The GL admin runs this manually + +# robustness: +# - if the "start" line exists, but the "end" line does not, you lose the +# rest of the existing authkey file. In general, "don't do that (TM)", +# but we do have a "vim -d" popping up so you can see the changes being +# made, just in case... + +# other notes: +# - you do NOT need to run this for permission changes within +# gitosis-lite.conf, (like giving an *existing* user new rights) +# - keys are added/deleted from the keystore **manually**, and all keys +# are named "name.pub" + +# command and options for authorized_keys +AUTH_COMMAND=~/.gitosis-lite/myecho +AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty" + +# save existing authkeys minus the GL-added stuff +sed -e '/^# gitosis-lite start/,/^# gitosis-lite end/d' \ + < ~/.ssh/authorized_keys \ + > ~/.ssh/new_authkeys + +# add our "start" line, each key on its own line (prefixed by command and +# options, in the standard ssh authorized_keys format), then the "end" line. +echo "# gitosis-lite start" >> ~/.ssh/new_authkeys +cd ~/.gitosis-lite/pubkeys +for i in *.pub +do + j=${i%.pub} + echo -n "command=\"$AUTH_COMMAND $j\",$AUTH_OPTIONS " + cat $i +done >> ~/.ssh/new_authkeys +echo "# gitosis-lite end" >> ~/.ssh/new_authkeys + +# just so you can see what changes are being made +vim -d ~/.ssh/authorized_keys ~/.ssh/new_authkeys + +# all done; overwrite the file (use cat to avoid perm changes) +cat ~/.ssh/new_authkeys > ~/.ssh/authorized_keys +rm ~/.ssh/new_authkeys + +# if the gl admin directory (~/.gitosis-lite) is itself a git repo, do an +# autocheckin. nothing fancy; this is a "just in case" type of thing. +cd ~/.gitosis-lite +if [[ -d .git ]] +then + git add -A pubkeys # stage all changes in pubkeys + if ! git diff --cached --quiet # and if there are any + then + echo pubkeys changed # create a commit message + echo + git diff --cached --name-status + fi | git commit -F - # and commit +fi From 40dbada4863c2ed6cbda4a3bdf260cf9221d0d52 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 Aug 2009 11:05:14 +0530 Subject: [PATCH 002/850] shell? perl? schizophrenia? fix it NOW dammit :) --- gl-add-auth-keys | 71 --------------------------------------- gl-compile-conf | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 71 deletions(-) delete mode 100755 gl-add-auth-keys create mode 100755 gl-compile-conf diff --git a/gl-add-auth-keys b/gl-add-auth-keys deleted file mode 100755 index dfadc1a..0000000 --- a/gl-add-auth-keys +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -# === add-auth-keys === -# refreshes ~/.ssh/authorized_keys from the list of pub-keys - -# part of the gitosis-lite (GL) suite - -# how run: manual, by GL admin -# when: anytime a pubkey is added/deleted -# (i.e., contents of ~/.gitosis-lite/pubkeys change) -# input: ~/.gitosis-lite/pubkeys -# output: ~/.ssh/authorized_keys -# security: -# - touches a very critical system file that manages the restrictions on -# incoming users. Be sure to audit AUTH_COMMAND and AUTH_OPTIONS (see -# below) on any change to this script -# - no security checks within program. The GL admin runs this manually - -# robustness: -# - if the "start" line exists, but the "end" line does not, you lose the -# rest of the existing authkey file. In general, "don't do that (TM)", -# but we do have a "vim -d" popping up so you can see the changes being -# made, just in case... - -# other notes: -# - you do NOT need to run this for permission changes within -# gitosis-lite.conf, (like giving an *existing* user new rights) -# - keys are added/deleted from the keystore **manually**, and all keys -# are named "name.pub" - -# command and options for authorized_keys -AUTH_COMMAND=~/.gitosis-lite/myecho -AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty" - -# save existing authkeys minus the GL-added stuff -sed -e '/^# gitosis-lite start/,/^# gitosis-lite end/d' \ - < ~/.ssh/authorized_keys \ - > ~/.ssh/new_authkeys - -# add our "start" line, each key on its own line (prefixed by command and -# options, in the standard ssh authorized_keys format), then the "end" line. -echo "# gitosis-lite start" >> ~/.ssh/new_authkeys -cd ~/.gitosis-lite/pubkeys -for i in *.pub -do - j=${i%.pub} - echo -n "command=\"$AUTH_COMMAND $j\",$AUTH_OPTIONS " - cat $i -done >> ~/.ssh/new_authkeys -echo "# gitosis-lite end" >> ~/.ssh/new_authkeys - -# just so you can see what changes are being made -vim -d ~/.ssh/authorized_keys ~/.ssh/new_authkeys - -# all done; overwrite the file (use cat to avoid perm changes) -cat ~/.ssh/new_authkeys > ~/.ssh/authorized_keys -rm ~/.ssh/new_authkeys - -# if the gl admin directory (~/.gitosis-lite) is itself a git repo, do an -# autocheckin. nothing fancy; this is a "just in case" type of thing. -cd ~/.gitosis-lite -if [[ -d .git ]] -then - git add -A pubkeys # stage all changes in pubkeys - if ! git diff --cached --quiet # and if there are any - then - echo pubkeys changed # create a commit message - echo - git diff --cached --name-status - fi | git commit -F - # and commit -fi diff --git a/gl-compile-conf b/gl-compile-conf new file mode 100755 index 0000000..a954744 --- /dev/null +++ b/gl-compile-conf @@ -0,0 +1,86 @@ +#!/usr/bin/perl -w + +use strict; + +# === add-auth-keys === +# refreshes ~/.ssh/authorized_keys from the list of pub-keys + +# part of the gitosis-lite (GL) suite + +# how run: manual, by GL admin +# when: anytime a pubkey is added/deleted +# (i.e., contents of ~/.gitosis-lite/keydir change) +# input: ~/.gitosis-lite/keydir +# output: ~/.ssh/authorized_keys +# security: +# - touches a very critical system file that manages the restrictions on +# incoming users. Be sure to audit AUTH_COMMAND and AUTH_OPTIONS (see +# below) on any change to this script +# - no security checks within program. The GL admin runs this manually + +# robustness: +# - if the "start" line exists, but the "end" line does not, you lose the +# rest of the existing authkey file. In general, "don't do that (TM)", +# but we do have a "vim -d" popping up so you can see the changes being +# made, just in case... + +# other notes: +# - you do NOT need to run this for permission changes within +# gitosis-lite.conf, (like giving an *existing* user new rights) +# - keys are added/deleted from the keystore **manually**, and all keys +# are named "name.pub" + +# command and options for authorized_keys +our $AUTH_COMMAND=$ENV{HOME} . "/.gitosis-lite/gl-auth-command"; +our $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; + +# quick subroutines +sub my_chdir +{ + chdir($_[0]) or die "chdir $_[0] failed: $!"; +} + +open(INF, "<", $ENV{HOME} . "/.ssh/authorized_keys") or die "open old authkeys failed: $!"; +open(OUT, ">", $ENV{HOME} . "/.ssh/new_authkeys") or die "open new authkeys failed: $!"; +# save existing authkeys minus the GL-added stuff +while () +{ + print OUT unless (/^# gitosis-lite start/../^# gitosis-lite end/); +} + +# add our "start" line, each key on its own line (prefixed by command and +# options, in the standard ssh authorized_keys format), then the "end" line. +print OUT "# gitosis-lite start\n"; +my_chdir($ENV{HOME} . "/.gitosis-lite/keydir"); +for my $pubkey (glob("*.pub")) +{ + my $user = $pubkey; $user =~ s/\.pub$//; + print OUT "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; + print OUT `cat $pubkey`; +} +print OUT "# gitosis-lite end\n"; +close(OUT); + +# check what changes are being made; just a comfort factor +system("vim -d ~/.ssh/authorized_keys ~/.ssh/new_authkeys"); + +# all done; overwrite the file (use cat to avoid perm changes) +system("cat ~/.ssh/new_authkeys > ~/.ssh/authorized_keys"); +system("rm ~/.ssh/new_authkeys"); + +# if the gl admin directory (~/.gitosis-lite) is itself a git repo, do an +# autocheckin. nothing fancy; this is a "just in case" type of thing. +my_chdir($ENV{HOME} . "/.gitosis-lite"); +if (-d ".git") +{ + system("git add -A keydir"); # stage all changes in keydir + if (! system("git diff --cached --quiet") ) + # and if there are any + { + open(COMMIT, "|-", "git commit -F -") + or die "pipe commit failed: $!"; + print COMMIT "keydir changed\n\n"; + print COMMIT `git diff --cached --name-status`; + close(COMMIT) or die "close commit failed: $!"; + } +} From d45a4e3972a2abc449567534b38f13bb529886ee Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 Aug 2009 11:29:07 +0530 Subject: [PATCH 003/850] added example.conf --- example.conf | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 example.conf diff --git a/example.conf b/example.conf new file mode 100644 index 0000000..f72f470 --- /dev/null +++ b/example.conf @@ -0,0 +1,61 @@ +# example conf file for gitosis-lite + +# objectives, over and above gitosis: +# - simpler syntax +# - no gitweb/daemon control +# - allows ff/non-ff control +# - allows branch level control + +# ---------------------------------------------------------------------------- +# user groups; syntax: +# @groupname = username [...] +# too many users? just add more such lines +# (they accumulate, like squid ACLs) + +@customers = cust1 cust2 +@customers = cust99 +@staff = me alice +@staff = bob +@secret_staff = bruce whitfield martin + +# ---------------------------------------------------------------------------- +# each section pertains to one or more repo(s); syntax: +# repo reponame [...] +# (R|RW|RW+) [list of ref names] = [list of users] + +# - RW+ means non-ff push is allowed +# - if no ref name exists, the rule applies to all refs +# - ref names are perl regex patterns +# - suffixed by "$" +# - prefixed by "refs/heads/" if it doesn't start with "refs/" +# (i.e., tags have to be explicitly named as refs/tags/pattern) + +# - the order of entries matters: the first ref+action line (for the repo) +# that matches is what counts. If the user is listed, the action is +# allowed, otherwise it is rejected. + +# - list of users can use any group name defined earlier +# - "@all" is a special, predefined, groupname + +# anyone can play in the sandbox +repo sandbox + RW+ = @all + +# my repo and alice's repo have the same memberships and access, so we just +# club them +repo myrepo alicerepo + RW+ = me, alice + R = bob, eve + +repo custrepo + RW = @customers + R = @staff + +# idea for the tags syntax shamelessly copied from git.git +# Documentation/howto/update-hook-example.txt :) +repo secret + RW+ pu = bruce + RW master next = bruce + RW refs/tags/v[0-9].* = bruce + RW tmp/.* = @secret_staff + R = @secret_staff From 930fbafed7532744b20fdb2459e6e5aa30537b20 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 Aug 2009 13:44:46 +0530 Subject: [PATCH 004/850] example conf expanded a bit --- example.conf | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/example.conf b/example.conf index f72f470..b4134f6 100644 --- a/example.conf +++ b/example.conf @@ -1,5 +1,7 @@ # example conf file for gitosis-lite +# comments in the normal shell-ish style; no surprises there + # objectives, over and above gitosis: # - simpler syntax # - no gitweb/daemon control @@ -14,8 +16,10 @@ @customers = cust1 cust2 @customers = cust99 +@interns = indy james @staff = me alice -@staff = bob +# you can nest groups; they'll be fully expanded in actual use +@staff = bob @interns @secret_staff = bruce whitfield martin # ---------------------------------------------------------------------------- @@ -44,8 +48,8 @@ repo sandbox # my repo and alice's repo have the same memberships and access, so we just # club them repo myrepo alicerepo - RW+ = me, alice - R = bob, eve + RW+ = me alice + R = bob eve repo custrepo RW = @customers From dc4193e633a2e781e3d9a63ec664e175681cd660 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 Aug 2009 13:46:45 +0530 Subject: [PATCH 005/850] gl-compile-conf now has "compilation" of the conf file also --- gl-compile-conf | 132 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 122 insertions(+), 10 deletions(-) diff --git a/gl-compile-conf b/gl-compile-conf index a954744..0d2283b 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -1,45 +1,85 @@ #!/usr/bin/perl -w use strict; +use Data::Dumper; # === add-auth-keys === -# refreshes ~/.ssh/authorized_keys from the list of pub-keys # part of the gitosis-lite (GL) suite +# (1) - "compiles" ~/.ssh/authorized_keys from the list of pub-keys +# (2) - also "compiles" the user-friendly GL conf file into something easier +# to parse. We're doing this because both the gl-auth-command and the +# (gl-)update hook need this, and it seems easier to do this than +# replicate the parsing code in both those places. As a bonus, it's +# probably more efficient. + # how run: manual, by GL admin -# when: anytime a pubkey is added/deleted -# (i.e., contents of ~/.gitosis-lite/keydir change) -# input: ~/.gitosis-lite/keydir -# output: ~/.ssh/authorized_keys +# when: +# - anytime a pubkey is added/deleted (i.e., contents of +# ~/.gitosis-lite/keydir change) +# - anytime gitosis-lite.conf is changed +# input: +# - ~/.gitosis-lite/gitosis-lite.conf +# - ~/.gitosis-lite/keydir +# output: +# - ~/.ssh/authorized_keys +# - ~/.ssh/gitosis-lite.conf-compiled.pm # security: # - touches a very critical system file that manages the restrictions on # incoming users. Be sure to audit AUTH_COMMAND and AUTH_OPTIONS (see # below) on any change to this script # - no security checks within program. The GL admin runs this manually -# robustness: +# warnings: # - if the "start" line exists, but the "end" line does not, you lose the # rest of the existing authkey file. In general, "don't do that (TM)", # but we do have a "vim -d" popping up so you can see the changes being # made, just in case... # other notes: -# - you do NOT need to run this for permission changes within -# gitosis-lite.conf, (like giving an *existing* user new rights) # - keys are added/deleted from the keystore **manually**, and all keys -# are named "name.pub" +# are named "name.pub". Keep the names simple. # command and options for authorized_keys our $AUTH_COMMAND=$ENV{HOME} . "/.gitosis-lite/gl-auth-command"; our $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; +our %groups = (); +our %repos = (); + # quick subroutines sub my_chdir { chdir($_[0]) or die "chdir $_[0] failed: $!"; } +sub expand_userlist +{ + my @list = @_; + my @new_list = (); + + for my $item (@list) + { + if ($item =~ /^@/) # nested group + { + die "undefined group $item" unless $groups{$item}; + # add those names to the list + push @new_list, @{ $groups{$item} }; + } + else + { + push @new_list, $item; + } + } + + return @new_list; +} + +# ---------------------------------------------------------------------------- +# "compile" ssh authorized_keys +# ---------------------------------------------------------------------------- + open(INF, "<", $ENV{HOME} . "/.ssh/authorized_keys") or die "open old authkeys failed: $!"; open(OUT, ">", $ENV{HOME} . "/.ssh/new_authkeys") or die "open new authkeys failed: $!"; # save existing authkeys minus the GL-added stuff @@ -62,7 +102,7 @@ print OUT "# gitosis-lite end\n"; close(OUT); # check what changes are being made; just a comfort factor -system("vim -d ~/.ssh/authorized_keys ~/.ssh/new_authkeys"); +# system("vim -d ~/.ssh/authorized_keys ~/.ssh/new_authkeys"); # all done; overwrite the file (use cat to avoid perm changes) system("cat ~/.ssh/new_authkeys > ~/.ssh/authorized_keys"); @@ -84,3 +124,75 @@ if (-d ".git") close(COMMIT) or die "close commit failed: $!"; } } + +# ---------------------------------------------------------------------------- +# "compile" GL conf +# ---------------------------------------------------------------------------- + +open(INF, "<", $ENV{HOME} . "/.gitosis-lite/gitosis-lite.conf") + or die "open GL conf failed: $!"; +open(OUT, ">", $ENV{HOME} . "/.ssh/gitosis-lite.conf-compiled.pm") + or die "open GL conf compiled failed: $!"; + +# the syntax is fairly simple, so we parse it inline + +my @repos; +while () +{ + # normalise whitespace; keeps later regexes very simple + s/=/ = /; + s/\s+/ /g; + s/^ //; + s/ $//; + # kill comments + s/#.*//; + # and blank lines + next unless /\S/; + + + # user groups + if (/^(@\S+) = (.*)/) + { + push @{ $groups{$1} }, expand_userlist( split(' ', $2) ); + } + # repo(s) + elsif (/^repo (.*)/) + { + @repos = split(' ', $1); + } + # actual permission line + elsif (/^(R|RW|RW\+) (.* )?= (.+)/) + { + my @perms = split //, $1; + my @refs = split ' ', $2 if $2; + my @users = split ' ', $3; + + # if no ref is given, this PERM applies to all refs + @refs = qw(refs/.*) unless @refs; + # fully qualify refs that dont start with "refs/"; prefix them with + # "refs/heads/" + @refs = map { m(^refs/) or s(^)(refs/heads/) } @refs; + + # expand the user list, unless it is just "@all" + @users = expand_userlist ( @users ) + unless (@users == 1 and $users[0] eq '@all'); + + # ok, we can finally populate the %repos hash + for my $repo (@repos) # each repo in the current stanza + { + for my $perm (@perms) + { + for my $ref (@refs) + { + for my $user (@users) + { + $repos{$repo}{$perm}{$ref}{$user} = 1; + } + } + } + } + } +} + +print OUT Data::Dumper->Dump([\%repos], [qw(*repos)]); +close(OUT); From 7d016908bd0a4ed08fcd6928411636a27071c463 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 Aug 2009 14:55:50 +0530 Subject: [PATCH 006/850] gl-compile-conf changed (see below) and "rc" file added - factored out all the pathnames etc to an rc - taught it to create repos that dont exist but are mentioned - promoted user up one level (moving ref down) because gl-auth needs it - REPO_BASE no longer contains $HOME so that has to be added in manually - little bugs here and there, like in @refs --- example.gitosis-lite.rc | 14 +++++++ gl-compile-conf | 82 ++++++++++++++++++++++++++++++----------- 2 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 example.gitosis-lite.rc diff --git a/example.gitosis-lite.rc b/example.gitosis-lite.rc new file mode 100644 index 0000000..2e9db31 --- /dev/null +++ b/example.gitosis-lite.rc @@ -0,0 +1,14 @@ +# this is meant to be pulled into a perl program using "do" + +# gitosis-lite admin directory, files, etc +$GL_ADMINDIR=$ENV{HOME} . "/.gitosis-lite"; +$GL_CONF="$GL_ADMINDIR/gitosis-lite.conf"; +$GL_KEYDIR="$GL_ADMINDIR/keydir"; + # this one has to agree with the other programs, watch out: +$GL_CONF_COMPILED=$ENV{HOME} . "/.ssh/gitosis-lite.conf-compiled.pm"; + +# base directory for all the repos +$REPO_BASE="repositories"; + +# this should be the last line in this file +1; diff --git a/gl-compile-conf b/gl-compile-conf index 0d2283b..bcce972 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -16,15 +16,14 @@ use Data::Dumper; # how run: manual, by GL admin # when: -# - anytime a pubkey is added/deleted (i.e., contents of -# ~/.gitosis-lite/keydir change) +# - anytime a pubkey is added/deleted # - anytime gitosis-lite.conf is changed # input: -# - ~/.gitosis-lite/gitosis-lite.conf -# - ~/.gitosis-lite/keydir +# - GL_CONF (default: ~/.gitosis-lite/gitosis-lite.conf) +# - GL_KEYDIR (default: ~/.gitosis-lite/keydir) # output: -# - ~/.ssh/authorized_keys -# - ~/.ssh/gitosis-lite.conf-compiled.pm +# - ~/.ssh/authorized_keys (dictated by sshd) +# - GL_CONF_COMPILED (default: ~/.gitosis-lite/gitosis-lite.conf-compiled.pm) # security: # - touches a very critical system file that manages the restrictions on # incoming users. Be sure to audit AUTH_COMMAND and AUTH_OPTIONS (see @@ -37,18 +36,39 @@ use Data::Dumper; # but we do have a "vim -d" popping up so you can see the changes being # made, just in case... -# other notes: -# - keys are added/deleted from the keystore **manually**, and all keys -# are named "name.pub". Keep the names simple. +# ---------------------------------------------------------------------------- +# common definitions +# ---------------------------------------------------------------------------- + +our $GL_ADMINDIR; +our $GL_CONF; +our $GL_KEYDIR; +our $GL_CONF_COMPILED; +our $REPO_BASE; + +my $glrc = $ENV{HOME} . "/.gitosis-lite.rc"; +unless (my $ret = do $glrc) +{ + die "parse $glrc failed: $@" if $@; + die "couldn't do $glrc: $!" unless defined $ret; + die "couldn't run $glrc" unless $ret; +} + +# ---------------------------------------------------------------------------- +# definitions specific to this program +# ---------------------------------------------------------------------------- # command and options for authorized_keys -our $AUTH_COMMAND=$ENV{HOME} . "/.gitosis-lite/gl-auth-command"; +our $AUTH_COMMAND="$GL_ADMINDIR/gl-auth-command"; our $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; our %groups = (); our %repos = (); -# quick subroutines +# ---------------------------------------------------------------------------- +# subroutines +# ---------------------------------------------------------------------------- + sub my_chdir { chdir($_[0]) or die "chdir $_[0] failed: $!"; @@ -91,7 +111,7 @@ while () # add our "start" line, each key on its own line (prefixed by command and # options, in the standard ssh authorized_keys format), then the "end" line. print OUT "# gitosis-lite start\n"; -my_chdir($ENV{HOME} . "/.gitosis-lite/keydir"); +my_chdir($GL_KEYDIR); for my $pubkey (glob("*.pub")) { my $user = $pubkey; $user =~ s/\.pub$//; @@ -110,12 +130,12 @@ system("rm ~/.ssh/new_authkeys"); # if the gl admin directory (~/.gitosis-lite) is itself a git repo, do an # autocheckin. nothing fancy; this is a "just in case" type of thing. -my_chdir($ENV{HOME} . "/.gitosis-lite"); +my_chdir($GL_ADMINDIR); if (-d ".git") { system("git add -A keydir"); # stage all changes in keydir - if (! system("git diff --cached --quiet") ) # and if there are any + if (system("git diff --cached --quiet") ) { open(COMMIT, "|-", "git commit -F -") or die "pipe commit failed: $!"; @@ -129,9 +149,9 @@ if (-d ".git") # "compile" GL conf # ---------------------------------------------------------------------------- -open(INF, "<", $ENV{HOME} . "/.gitosis-lite/gitosis-lite.conf") +open(INF, "<", $GL_CONF) or die "open GL conf failed: $!"; -open(OUT, ">", $ENV{HOME} . "/.ssh/gitosis-lite.conf-compiled.pm") +open(OUT, ">", $GL_CONF_COMPILED) or die "open GL conf compiled failed: $!"; # the syntax is fairly simple, so we parse it inline @@ -164,14 +184,14 @@ while () elsif (/^(R|RW|RW\+) (.* )?= (.+)/) { my @perms = split //, $1; - my @refs = split ' ', $2 if $2; + my @refs; @refs = split(' ', $2) if $2; my @users = split ' ', $3; # if no ref is given, this PERM applies to all refs @refs = qw(refs/.*) unless @refs; # fully qualify refs that dont start with "refs/"; prefix them with # "refs/heads/" - @refs = map { m(^refs/) or s(^)(refs/heads/) } @refs; + @refs = map { m(^refs/) or s(^)(refs/heads/); $_ } @refs; # expand the user list, unless it is just "@all" @users = expand_userlist ( @users ) @@ -182,11 +202,11 @@ while () { for my $perm (@perms) { - for my $ref (@refs) + for my $user (@users) { - for my $user (@users) + for my $ref (@refs) { - $repos{$repo}{$perm}{$ref}{$user} = 1; + $repos{$repo}{$perm}{$user}{$ref} = 1; } } } @@ -196,3 +216,23 @@ while () print OUT Data::Dumper->Dump([\%repos], [qw(*repos)]); close(OUT); + +# ---------------------------------------------------------------------------- +# any new repos created? +# ---------------------------------------------------------------------------- + +# modern gits allow cloning from an empty repo, so we just create it. Gitosis +# did not have that luxury, so it was forced to detect the first push and +# create it then + +my_chdir("$ENV{HOME}/$REPO_BASE"); +for my $repo (keys %repos) +{ + unless (-d "$repo.git") + { + mkdir("$repo.git") or die "mkdir $repo.git failed: $!"; + my_chdir("$repo.git"); + system("git init --bare"); + my_chdir("$ENV{HOME}/$REPO_BASE"); + } +} From 4a4b6e9d97794fed3675f317248bb465fa31681b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 Aug 2009 18:24:37 +0530 Subject: [PATCH 007/850] auth: finally! --- gl-auth-command | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 gl-auth-command diff --git a/gl-auth-command b/gl-auth-command new file mode 100755 index 0000000..e1be7d7 --- /dev/null +++ b/gl-auth-command @@ -0,0 +1,106 @@ +#!/usr/bin/perl -w + +use strict; + +# === auth-command === +# the command that GL users actually run + +# part of the gitosis-lite (GL) suite + +# how run: via sshd, being listed in "command=" in ssh authkeys +# when: every login by a GL user +# input: $1 is GL username, plus $SSH_ORIGINAL_COMMAND +# output: +# security: +# - currently, we just make some basic checks, copied from gitosis + +# robustness: + +# other notes: + +# ---------------------------------------------------------------------------- +# common definitions +# ---------------------------------------------------------------------------- + +our $GL_ADMINDIR; +our $GL_CONF; +our $GL_KEYDIR; +our $GL_CONF_COMPILED; +our $REPO_BASE; +our %repos; + +my $glrc = $ENV{HOME} . "/.gitosis-lite.rc"; +unless (my $ret = do $glrc) +{ + die "parse $glrc failed: $@" if $@; + die "couldn't do $glrc: $!" unless defined $ret; + die "couldn't run $glrc" unless $ret; +} + +die "couldnt do perms file" unless (my $ret = do $GL_CONF_COMPILED); + +# ---------------------------------------------------------------------------- +# definitions specific to this program +# ---------------------------------------------------------------------------- + +our $R_COMMANDS=qr/git[ -]upload-pack/; +our $W_COMMANDS=qr/git[ -]receive-pack/; +our $REPONAME_PATT=qr(^[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern + +# ---------------------------------------------------------------------------- +# start... +# ---------------------------------------------------------------------------- + +# first, fix the biggest gripe I have with gitosis, a 1-line change +my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! + +# ---------------------------------------------------------------------------- +# sanity checks on SSH_ORIGINAL_COMMAND +# ---------------------------------------------------------------------------- + +# SSH_ORIGINAL_COMMAND must exist. Since we also captured $user, we print +# that in the message so people saying "ssh git@server" can see which gitosis +# user he is being recognised as +my $cmd = $ENV{SSH_ORIGINAL_COMMAND} + or die "no SSH_ORIGINAL_COMMAND? I'm not a shell, $user!"; + +# we don't like newlines or semicolons in SSH_ORIGINAL_COMMAND +die "$cmd??? you're a funny guy..." + if $cmd =~ /[;\n]/; + +# split into command and arguments; the pattern allows old style as well as +# new style: "git-subcommand arg" or "git subcommand arg", just like gitosis +# does, although I'm not sure how necessary that is +# +# keep in mind this is how git sends across the command: +# git-receive-pack 'reponame.git' +# including the single quotes + +my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'(.*).git'/); +die "$verb? I don't do odd jobs, sorry..." + unless $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS; + +die "I don't like the look of $repo, sorry!" + unless $repo =~ $REPONAME_PATT; + +# ---------------------------------------------------------------------------- +# first level permissions check +# ---------------------------------------------------------------------------- + +# now, knowing the user and repo (which is repo path), we try perms +my $perm = 'W'; $perm = 'R' if $verb =~ $R_COMMANDS; +die "access denied" unless $repos{$repo}{$perm}{$user}; + +# ---------------------------------------------------------------------------- +# over to git now +# ---------------------------------------------------------------------------- + +# ( but first save the reponame; we can save some time later in the hook ) +$ENV{GL_REPO}=$repo; + +open(LOG, ">>", "$GL_ADMINDIR/log"); +print LOG "\n", scalar(localtime), " $ENV{SSH_ORIGINAL_COMMAND} $user\n"; +close(LOG); + +$repo = "'$REPO_BASE/$repo.git'"; +exec("git", "shell", "-c", "$verb $repo"); From 2e38867b59ac68b927f1671ff9e8c2bc2fd3a2be Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 Aug 2009 21:40:03 +0530 Subject: [PATCH 008/850] add hook, the last piece --- gl-compile-conf | 3 ++ update-hook.pl | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100755 update-hook.pl diff --git a/gl-compile-conf b/gl-compile-conf index bcce972..6aefe0c 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -225,6 +225,7 @@ close(OUT); # did not have that luxury, so it was forced to detect the first push and # create it then +umask(0077); my_chdir("$ENV{HOME}/$REPO_BASE"); for my $repo (keys %repos) { @@ -233,6 +234,8 @@ for my $repo (keys %repos) mkdir("$repo.git") or die "mkdir $repo.git failed: $!"; my_chdir("$repo.git"); system("git init --bare"); + system("cp $GL_ADMINDIR/update-hook.pl hooks/update"); + system("chmod 755 hooks/update"); my_chdir("$ENV{HOME}/$REPO_BASE"); } } diff --git a/update-hook.pl b/update-hook.pl new file mode 100755 index 0000000..53540e9 --- /dev/null +++ b/update-hook.pl @@ -0,0 +1,82 @@ +#!/usr/bin/perl -w + +use strict; + +# === update === +# this is gitosis-lite's update hook + +# part of the gitosis-lite (GL) suite + +# how run: via git, being copied as .git/hooks/update in every repo +# when: every push +# input: +# - see man githooks for STDIN +# - uses the compiled config file to get permissions info +# output: based on permissions etc., exit 0 or 1 +# security: +# - none + +# robustness: + +# other notes: + +# ---------------------------------------------------------------------------- +# common definitions +# ---------------------------------------------------------------------------- + +our $GL_ADMINDIR; +our $GL_CONF; +our $GL_KEYDIR; +our $GL_CONF_COMPILED; +our $REPO_BASE; +our %repos; + +my $glrc = $ENV{HOME} . "/.gitosis-lite.rc"; +unless (my $ret = do $glrc) +{ + die "parse $glrc failed: $@" if $@; + die "couldn't do $glrc: $!" unless defined $ret; + die "couldn't run $glrc" unless $ret; +} + +die "couldnt do perms file" unless (my $ret = do $GL_CONF_COMPILED); + +# ---------------------------------------------------------------------------- +# definitions specific to this program +# ---------------------------------------------------------------------------- + +open(LOG, ">>", "$GL_ADMINDIR/log"); + +# ---------------------------------------------------------------------------- +# start... +# ---------------------------------------------------------------------------- + +my $ref = shift; +my $oldsha = shift; +my $newsha = shift; +my $merge_base = '0' x 40; +chomp($merge_base = `git merge-base $oldsha $newsha`) + unless $oldsha eq '0' x 40; + +# some of this is from an example hook in Documentation/howto of git.git, with +# some variations + +# what are you trying to do? (is it 'W' or '+'?) +my $perm = 'W'; +# rewriting a tag +$perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); +# non-ff push to branch +$perm = '+' if $ref =~ m(refs/heads/) and $oldsha ne $merge_base; + +my $allowed_refs = $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}}; +for my $refex (keys %$allowed_refs) +# refex? sure -- a regex to match a ref against :) +{ + if ($ref =~ /$refex/) + { + print LOG "$perm: $ENV{GL_USER} $ENV{GL_REPO} $ref $oldsha $newsha\n"; + close (LOG); + exit 0; + } +} +exit 1; From e8e7bda41c9547d0f074d92090699b3847325ca7 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 24 Aug 2009 07:29:25 +0530 Subject: [PATCH 009/850] added README and TODO --- README.markdown | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ TODO | 9 +++++++ 2 files changed, 80 insertions(+) create mode 100644 README.markdown create mode 100644 TODO diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..d63b62d --- /dev/null +++ b/README.markdown @@ -0,0 +1,71 @@ +# gitosis-lite + +In this document: + + * "lite"? + * what's extra + * whats missing/TODO + * workflow + * conf file example + +---- + +### "lite"? + +I have been gitosis for a while, and have learnt a lot from it. But in a +typical $DAYJOB setting, there are some issues. It's not always Linux, so you +can't just "urpmi gitosis" and be done. "python-setuptools" isn't often +installed (and on a Solaris 9 I was trying to help remotely, we never did +manage it). And the most requested feature (see next section) had to be +written anyway. + +While I was pondering having to finally learn python (I hate whitespace based +flow logic except for plain text; this is a *personal* opinion so pythonistas +can back off :-), I also realised that: + + * no one in $DAYJOB settings will use or approve access methods that work + without any authentication, so I didn't need gitweb/daemon support in the + tool + * the idea that you admin it by pushing to a special repo is cute and + convenient, but not really necessary because of how rarely these changes + are made. + +All of this pointed to a rewrite. In perl, naturally. + +I also gained (and used) an unfair advantage: gits newer than 1.6.2 can clone +an empty repo, so I don't need complex logic in the permissions checking part +to *create* the repo initially -- I just create an empty bare repo when I +"compile" the config file (see "workflow" below). + +### what's extra? + +A lot of people in my $DAYJOB type world want per-branch permissions, so I +copied the basic idea from +git.git:Documentation/howto/update-hook-example.txt. I think this is the most +significant extra I have. This includes not just who can push to what branch, +but also whether they are allowed to rewind it or not (non-ff push). + +### what's missing/TODO + +See TODO file + +### workflow + +I took the opportunity to change the workflow significantly. + + * all admin happens *on the server*, in a special directory + * after making any changes, one "compiles" the configuration. This + refreshes `~/.ssh/authorized_keys`, as well as puts a parsed form of the + access list in a file for the other two pieces to use. + +Why pre-parse? Because access control decisions are taken at two separate +stages now: + + * the program that is run via `~/.ssh/authorized_keys` (called + `gl-auth-command`, equivalent to `gitosis-serve`) decides whether even git + should be allowed to run (basic R/W/no access) + * the update-hook on each repo, which decides the per-branch permissions. + +But the user specifies only one access file, and he doesn't have to know these +distinctions. So I avoid having to parse the access file in two completely +different programs by pre-compiling it and storing it as a perl "variable". diff --git a/TODO b/TODO new file mode 100644 index 0000000..c3bfd57 --- /dev/null +++ b/TODO @@ -0,0 +1,9 @@ + * a proper INSTALL file with clear instructions for non-experts + + * make a proper test suite + + * `use Git` instead of run git commands: I don't run too many git commands + in this and Git.pm just runs the same commands (per the documentation), so + it's sorta moot right now, but it's worth trying + + * change the "rc" file to use "gitconfig" instead... From 14f82ffc462793018894b265f8403f1ee5473175 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 24 Aug 2009 10:07:06 +0530 Subject: [PATCH 010/850] INSTALL started (w-i-p for now); minor changes to README, TODO, and the rc file --- INSTALL | 25 +++++++++++++++++++++++++ README.markdown | 6 ------ TODO | 2 ++ example.gitosis-lite.rc | 22 ++++++++++++++-------- 4 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 INSTALL diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..2a0184a --- /dev/null +++ b/INSTALL @@ -0,0 +1,25 @@ +There are 5 files you need to touch/copy + + example.conf + example.gitosis-lite.rc + gl-auth-command + gl-compile-conf + update-hook.pl + +1. copy `example.gitosis-lite.rc` as `~/.gitosis-lite.rc`. This location is + fixed for now (maybe later I'll change it to a "git config" variable). + +2. edit `~/.gitosis-lite.rc` and change all the paths however you want. Be + sure to keep the perl syntax -- you *don't* have to know perl to do so, + it's fairly easy to guess in this limited case. + +3. copy `example.conf` to whatever path you specified for `GL_CONF` in the rc + file in step 2. By default it is `~/.gitosis-lite/gitosis-lite.conf`. + + Edit the file as you wish. The comments in the file ought to be clear + enough but let me know if not + +4. create directories for whatever you named in `GL_KEYDIR` and `REPO_BASE` + (default `~/.gitosis-lite/keydir` and `~/repositories`) + +5. copy `update-hook.pl` to `$GL_ADMINDIR` (default `~/.gitosis-lite`) diff --git a/README.markdown b/README.markdown index d63b62d..53413f6 100644 --- a/README.markdown +++ b/README.markdown @@ -4,9 +4,7 @@ In this document: * "lite"? * what's extra - * whats missing/TODO * workflow - * conf file example ---- @@ -45,10 +43,6 @@ git.git:Documentation/howto/update-hook-example.txt. I think this is the most significant extra I have. This includes not just who can push to what branch, but also whether they are allowed to rewind it or not (non-ff push). -### what's missing/TODO - -See TODO file - ### workflow I took the opportunity to change the workflow significantly. diff --git a/TODO b/TODO index c3bfd57..7f3a611 100644 --- a/TODO +++ b/TODO @@ -7,3 +7,5 @@ it's sorta moot right now, but it's worth trying * change the "rc" file to use "gitconfig" instead... + + * check user/group names with some suitable regex diff --git a/example.gitosis-lite.rc b/example.gitosis-lite.rc index 2e9db31..ddbcca8 100644 --- a/example.gitosis-lite.rc +++ b/example.gitosis-lite.rc @@ -1,14 +1,20 @@ # this is meant to be pulled into a perl program using "do" -# gitosis-lite admin directory, files, etc -$GL_ADMINDIR=$ENV{HOME} . "/.gitosis-lite"; -$GL_CONF="$GL_ADMINDIR/gitosis-lite.conf"; -$GL_KEYDIR="$GL_ADMINDIR/keydir"; - # this one has to agree with the other programs, watch out: -$GL_CONF_COMPILED=$ENV{HOME} . "/.ssh/gitosis-lite.conf-compiled.pm"; - # base directory for all the repos $REPO_BASE="repositories"; -# this should be the last line in this file +# gitosis-lite admin directory, files, etc +$GL_ADMINDIR=$ENV{HOME} . "/.gitosis-lite"; + +# -------------------------------------- + +# the ones below can be left as they are, unless for some reason you want them +# elsewhere + +$GL_CONF="$GL_ADMINDIR/gitosis-lite.conf"; +$GL_KEYDIR="$GL_ADMINDIR/keydir"; +$GL_CONF_COMPILED="$GL_ADMINDIR/gitosis-lite.conf-compiled.pm"; + +# -------------------------------------- +# this should be the last line in this file, per perl rules 1; From 6feffc9b165a724bf264763ce3d3099c1287c1e1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 24 Aug 2009 10:07:33 +0530 Subject: [PATCH 011/850] example.conf got lots more comments (aka documentation!) --- example.conf | 56 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/example.conf b/example.conf index b4134f6..006ea7c 100644 --- a/example.conf +++ b/example.conf @@ -1,6 +1,9 @@ # example conf file for gitosis-lite -# comments in the normal shell-ish style; no surprises there +# overall syntax: +# - everything in this is space-separated; no commas, semicolons, etc +# - comments in the normal shell-ish style; no surprises there +# - there are no continuation lines of any kind # objectives, over and above gitosis: # - simpler syntax @@ -9,51 +12,61 @@ # - allows branch level control # ---------------------------------------------------------------------------- -# user groups; syntax: +# USERS and GROUPS + +# syntax: # @groupname = username [...] -# too many users? just add more such lines + +# usernames and groupnames should be as simple as possible; there's no +# explicit list of allowed characters for now but that's a TODO item. + +# too many users in one group? just add more such lines # (they accumulate, like squid ACLs) -@customers = cust1 cust2 -@customers = cust99 +@cust_A = cust1 cust2 +@cust_A = cust99 @interns = indy james @staff = me alice -# you can nest groups; they'll be fully expanded in actual use + +# you can nest groups, but not recursively of course! @staff = bob @interns @secret_staff = bruce whitfield martin # ---------------------------------------------------------------------------- -# each section pertains to one or more repo(s); syntax: -# repo reponame [...] -# (R|RW|RW+) [list of ref names] = [list of users] +# REPOS, REFS, and PERMISSIONS + +# syntax: +# repo [one or more reponames] +# (R|RW|RW+) [zero or more refnames] = [one or more users] + +# notes: # - RW+ means non-ff push is allowed -# - if no ref name exists, the rule applies to all refs +# - you can't write just "W" or "+"; it has to be R, or RW, or RW+ + +# - if no ref name appears, the rule applies to all refs in that repo # - ref names are perl regex patterns -# - suffixed by "$" # - prefixed by "refs/heads/" if it doesn't start with "refs/" # (i.e., tags have to be explicitly named as refs/tags/pattern) -# - the order of entries matters: the first ref+action line (for the repo) -# that matches is what counts. If the user is listed, the action is -# allowed, otherwise it is rejected. - -# - list of users can use any group name defined earlier +# - the list of users can inlude any group name defined earlier # - "@all" is a special, predefined, groupname -# anyone can play in the sandbox +# anyone can play in the sandbox, including making non-fastforward commits +# (that's what the "+" means) repo sandbox RW+ = @all # my repo and alice's repo have the same memberships and access, so we just -# club them +# put them both in the same stanza repo myrepo alicerepo RW+ = me alice R = bob eve -repo custrepo - RW = @customers - R = @staff +# this repo is visible to customers from company A but they can't write to it +repo cust_A_repo + R = @cust_A + RW = @staff # idea for the tags syntax shamelessly copied from git.git # Documentation/howto/update-hook-example.txt :) @@ -61,5 +74,6 @@ repo secret RW+ pu = bruce RW master next = bruce RW refs/tags/v[0-9].* = bruce + RW refs/tags/ = @secret_staff RW tmp/.* = @secret_staff R = @secret_staff From 3cddc4ca354cdd027d6eadecfde93f045418d980 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 24 Aug 2009 10:16:25 +0530 Subject: [PATCH 012/850] compile/update-hook: preserve the order of refs ...by turning the last piece (list of allowed refs) into an array rather than a hash --- gl-compile-conf | 5 +---- update-hook.pl | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/gl-compile-conf b/gl-compile-conf index 6aefe0c..8379fa5 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -204,10 +204,7 @@ while () { for my $user (@users) { - for my $ref (@refs) - { - $repos{$repo}{$perm}{$user}{$ref} = 1; - } + push @{ $repos{$repo}{$perm}{$user} }, @refs; } } } diff --git a/update-hook.pl b/update-hook.pl index 53540e9..b881f5a 100755 --- a/update-hook.pl +++ b/update-hook.pl @@ -69,7 +69,7 @@ $perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); $perm = '+' if $ref =~ m(refs/heads/) and $oldsha ne $merge_base; my $allowed_refs = $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}}; -for my $refex (keys %$allowed_refs) +for my $refex (@$allowed_refs) # refex? sure -- a regex to match a ref against :) { if ($ref =~ /$refex/) From d33c408dc3f1d54db81aa495e22bb0736001f342 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 24 Aug 2009 13:29:33 +0530 Subject: [PATCH 013/850] INSTALL and README pretty much done --- INSTALL | 106 ++++++++++++++++++++++++++++++++++++++------- README.markdown | 112 ++++++++++++++++++++++++++++++------------------ 2 files changed, 160 insertions(+), 58 deletions(-) diff --git a/INSTALL b/INSTALL index 2a0184a..ce0d1d0 100644 --- a/INSTALL +++ b/INSTALL @@ -1,25 +1,99 @@ -There are 5 files you need to touch/copy +### quickinstall - example.conf - example.gitosis-lite.rc - gl-auth-command - gl-compile-conf - update-hook.pl +I assume all the files pertaining to this software are untarred and available +in the current directory. -1. copy `example.gitosis-lite.rc` as `~/.gitosis-lite.rc`. This location is - fixed for now (maybe later I'll change it to a "git config" variable). +A quick install, taking all the defaults, can be done with the following +commands; just copy and paste them into your shell: -2. edit `~/.gitosis-lite.rc` and change all the paths however you want. Be - sure to keep the perl syntax -- you *don't* have to know perl to do so, - it's fairly easy to guess in this limited case. + # this one is fixed to the location shown + cp example.gitosis-lite.rc ~/.gitosis-lite.rc -3. copy `example.conf` to whatever path you specified for `GL_CONF` in the rc - file in step 2. By default it is `~/.gitosis-lite/gitosis-lite.conf`. + # the destinations below are defaults; if you change the paths in the "rc" + # file above, these destinations also must change accordingly + # mkdir $REPO_BASE, $GL_ADMINDIR, and $GL_KEYDIR + mkdir ~/repositories + mkdir ~/.gitosis-lite + mkdir ~/.gitosis-lite/keydir + + # copy sample conf to $GL_CONF + cp example.conf ~/.gitosis-lite/gitosis-lite.conf + + # copy the 3 programs to $GL_ADMINDIR + cp update-hook.pl ~/.gitosis-lite + cp gl-auth-command ~/.gitosis-lite + cp gl-compile-conf ~/.gitosis-lite + + # optional; copy the documents also (if you untarred the package into a + # temporary directory and need to get rid of it) + cp INSTALL README.markdown ~/.gitosis-lite + +### install notes + + * At present the location of `~/.gitosis-lite.rc` is fixed (maybe later I'll + change it to a "git config" variable). + + If you edit it and change any paths, be sure to keep the perl syntax -- + you *don't* have to know perl to do so, it's fairly easy to guess in this + limited case. And of course, make sure you adjust the commands shown + above to suit the new locations + + * the config file is (by default) at `~/.gitosis-lite/gitosis-lite.conf`. Edit the file as you wish. The comments in the file ought to be clear enough but let me know if not -4. create directories for whatever you named in `GL_KEYDIR` and `REPO_BASE` - (default `~/.gitosis-lite/keydir` and `~/repositories`) + * if you want to bring in existing (bare, server) repos into gitosis-lite, + this should work: + * backup the repo, then move it to `$BASE_REPO` + * copy `$GL_ADMINDIR/update-hook.pl` to `[reponame].git/hooks/update` -- + if you don't do this, per branch restrictions will not work + * then update the keys and the config file and "compile" -5. copy `update-hook.pl` to `$GL_ADMINDIR` (default `~/.gitosis-lite`) +### administer + + * ask each user who will get access to send you a public key. See other + sources (for example + [here](http://sitaramc.github.com/0-installing/2-access-gitosis.html#generating_a_public_key)) + for how to do this + * for each "user" in `$GL_CONF`, copy their public key to a file called + "user.pub" in `$GL_KEYDIR` + * edit the config file (`$GL_CONF`) to add the new users in whatever way you + like + * backup your `~/.ssh/authorized_keys` file if you feel nervous :-) + * cd to `$GL_ADMINDIR` and run `./gl-compile-conf` + +#### optional -- if you want to be doubly sure + +It should all work, but the first couple of times you may want to check these + + * check the outputs + + * `~/.ssh/authorized_keys` should contain one line for each "user" pub + key added, between two "marker" lines (which you should please please + not remove!). The line should contain a "command=" pointing to a + `$GL_ADMINDIR/gl-auth-command` file, then some sshd restrictions, the + key, etc. + * `$GL_CONF_COMPILED` (default + `~/.gitosis-lite/gitosis-lite.conf-compiled.pm`) should contain an + expanded list of the access control rules. It may look a little long, + but it's fairly intuitive! + + * if the run threw up any "initialising empty repo" messages, check the + individual repos (inside `$REPO_BASE`) if you wish. Especially make sure + the `$REPO_BASE/[reponame].git/hooks/update` got copied OK and is + executable + +### run + +Just use it as normal. Every new repo mentioned has been created already, so +(as long as your clients are using git > 1.6.2), you can just clone it. + +And once in a while, if you're feeling particularly BOFH-ish, take a look at +`$GL_ADMINDIR/log` :-) + +### errors, warnings, etc + + * when you clone an empty repo, git seems to complain about the remote + hanging up or something. I have no idea what that is, but it doesn't seem + to hurt anything. This happens even in normal git, not just gitosis-lite. diff --git a/README.markdown b/README.markdown index 53413f6..363378e 100644 --- a/README.markdown +++ b/README.markdown @@ -1,65 +1,93 @@ # gitosis-lite +gitosis-lite is the bare essentials of gitosis, with a completely different +config file that allows (at last!) access control down to the branch level, +including specifying who can and cannot *rewind* a given branch. + In this document: - * "lite"? - * what's extra - * workflow + * why + * what's gone + * what's new + * the workflow ---- -### "lite"? +### why -I have been gitosis for a while, and have learnt a lot from it. But in a -typical $DAYJOB setting, there are some issues. It's not always Linux, so you -can't just "urpmi gitosis" and be done. "python-setuptools" isn't often -installed (and on a Solaris 9 I was trying to help remotely, we never did -manage it). And the most requested feature (see next section) had to be -written anyway. +I have been using gitosis for a while, and have learnt a lot from it. But in +a typical $DAYJOB setting, there are some issues: -While I was pondering having to finally learn python (I hate whitespace based -flow logic except for plain text; this is a *personal* opinion so pythonistas -can back off :-), I also realised that: + * it's not always Linux; you can't just "urpmi gitosis" (or yum or apt-get) + and be done + * often, "python-setuptools" isn't installed (and on a Solaris9 I was trying + to help remotely, we never did manage to install it eventually) + * the most requested feature (see "what's extra?") had to be written anyway - * no one in $DAYJOB settings will use or approve access methods that work - without any authentication, so I didn't need gitweb/daemon support in the - tool - * the idea that you admin it by pushing to a special repo is cute and - convenient, but not really necessary because of how rarely these changes - are made. +### what's gone -All of this pointed to a rewrite. In perl, naturally. +While I was pondering the need to finally learn python[1] , I also realised +that: -I also gained (and used) an unfair advantage: gits newer than 1.6.2 can clone -an empty repo, so I don't need complex logic in the permissions checking part -to *create* the repo initially -- I just create an empty bare repo when I -"compile" the config file (see "workflow" below). + * no one in $DAYJOB type environments will use or approve access methods + that work without any authentication, so I didn't need gitweb/daemon + support in the tool or in the config file + * the idea that you admin it by pushing to a special repo is nice, but not + really necessary because of how rarely these changes are made, especially + considering how much code is involved in that piece + +All of this pointed to a rewrite. In perl, naturally :-) ### what's extra? -A lot of people in my $DAYJOB type world want per-branch permissions, so I -copied the basic idea from -git.git:Documentation/howto/update-hook-example.txt. I think this is the most -significant extra I have. This includes not just who can push to what branch, -but also whether they are allowed to rewind it or not (non-ff push). +Per-branch permissions. You will not believe how often I am asked this at +$DAYJOB. This is almost the single reason I started *thinking* about rolling +my own gitosis in the first place. -### workflow +Take a look at the example config file in the repo to see how I do this. I +copied the basic idea from `update-hook-example.txt` (it's one of the "howto"s +that come with the git source tree). This includes not just who can push to +what branch, but also whether they are allowed to rewind it or not (non-ff +push). -I took the opportunity to change the workflow significantly. +However, please note the difference in the size and complexity of the +*operational code* between the update hook in that example, and in mine :-) +The reason is in the next section. - * all admin happens *on the server*, in a special directory - * after making any changes, one "compiles" the configuration. This +### the workflow + +In order to get per-branch access, you *must* use an update hook. However, +that only gets invoked on a push; "read" access still has to be controlled +right at the beginning, before git even enters the scene (just the way gitosis +currently works). + +So: either split the access control into two config files, or have two +completely different programs *both* parse the same one and pick what they +want. Crap... I definitely don't want the hook doing any parsing, (and it +would be nice if the auth-control program didn't have to either). + +So I changed the workflow completely: + + * all admin changes happen *on the server*, in a special directory that + contains the config and the users' pubkeys. But there's no commit and + push afterward. Nothing prevents you from version-controlling that + directory if you wish to, but it's not *required* + * instead, after making changes, you "compile" the configuration. This refreshes `~/.ssh/authorized_keys`, as well as puts a parsed form of the access list in a file for the other two pieces to use. -Why pre-parse? Because access control decisions are taken at two separate -stages now: +The pre-parsed form is basically a huge perl variable. It's human readable +too (never mind what the python guys say!) - * the program that is run via `~/.ssh/authorized_keys` (called - `gl-auth-command`, equivalent to `gitosis-serve`) decides whether even git - should be allowed to run (basic R/W/no access) - * the update-hook on each repo, which decides the per-branch permissions. +Advantages: all the complexity of parsing and error checking the parse is done +away from the two places where the actual access control happens, which are: -But the user specifies only one access file, and he doesn't have to know these -distinctions. So I avoid having to parse the access file in two completely -different programs by pre-compiling it and storing it as a perl "variable". + * the program that is run via `~/.ssh/authorized_keys` (I call it + `gl-auth-command`, equivalent to `gitosis-serve`); this decides whether + git should even be allowed to run (basic R/W/no access) + * the update-hook on each repo, which decides the per-branch permissions + +---- + +[1] I hate whitespace to mean anything significant except for text; this is a +personal opinion *only*, so pythonistas please back off :-) From cd01bb52975f12a589c5c61f52a4fe804d364767 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 24 Aug 2009 13:30:58 +0530 Subject: [PATCH 014/850] compile: fail/error checks: - don't update authkeys if parse fails (done by moving that code so it runs *after* the parse) - check group/usernames for sanity --- example.conf | 9 +-- gl-compile-conf | 191 +++++++++++++++++++++++++----------------------- 2 files changed, 103 insertions(+), 97 deletions(-) diff --git a/example.conf b/example.conf index 006ea7c..89f3bd0 100644 --- a/example.conf +++ b/example.conf @@ -17,19 +17,18 @@ # syntax: # @groupname = username [...] -# usernames and groupnames should be as simple as possible; there's no -# explicit list of allowed characters for now but that's a TODO item. +# usernames and groupnames should be as simple as possible # too many users in one group? just add more such lines # (they accumulate, like squid ACLs) - @cust_A = cust1 cust2 @cust_A = cust99 -@interns = indy james -@staff = me alice # you can nest groups, but not recursively of course! +@interns = indy james @staff = bob @interns + +@staff = me alice @secret_staff = bruce whitfield martin # ---------------------------------------------------------------------------- diff --git a/gl-compile-conf b/gl-compile-conf index 8379fa5..ac4134d 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -61,6 +61,7 @@ unless (my $ret = do $glrc) # command and options for authorized_keys our $AUTH_COMMAND="$GL_ADMINDIR/gl-auth-command"; our $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; +our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern our %groups = (); our %repos = (); @@ -81,6 +82,7 @@ sub expand_userlist for my $item (@list) { + die "bad user $item\n" unless $item =~ $USERNAME_PATT; if ($item =~ /^@/) # nested group { die "undefined group $item" unless $groups{$item}; @@ -96,6 +98,103 @@ sub expand_userlist return @new_list; } +# ---------------------------------------------------------------------------- +# "compile" GL conf +# ---------------------------------------------------------------------------- + +open(INF, "<", $GL_CONF) + or die "open GL conf failed: $!"; + +# the syntax is fairly simple, so we parse it inline + +my @repos; +while () +{ + # normalise whitespace; keeps later regexes very simple + s/=/ = /; + s/\s+/ /g; + s/^ //; + s/ $//; + # kill comments + s/#.*//; + # and blank lines + next unless /\S/; + + # user groups + if (/^(@\S+) = (.*)/) + { + push @{ $groups{$1} }, expand_userlist( split(' ', $2) ); + die "bad group $1\n" unless $1 =~ $USERNAME_PATT; + } + # repo(s) + elsif (/^repo (.*)/) + { + @repos = split(' ', $1); + } + # actual permission line + elsif (/^(R|RW|RW\+) (.* )?= (.+)/) + { + # split perms to separate out R, W, and + + my @perms = split //, $1; + my @refs; @refs = split(' ', $2) if $2; + my @users = split ' ', $3; + + # if no ref is given, this PERM applies to all refs + @refs = qw(refs/.*) unless @refs; + # fully qualify refs that dont start with "refs/"; prefix them with + # "refs/heads/" + @refs = map { m(^refs/) or s(^)(refs/heads/); $_ } @refs; + + # expand the user list, unless it is just "@all" + @users = expand_userlist ( @users ) + unless (@users == 1 and $users[0] eq '@all'); + + # ok, we can finally populate the %repos hash + for my $repo (@repos) # each repo in the current stanza + { + for my $perm (@perms) + { + for my $user (@users) + { + push @{ $repos{$repo}{$perm}{$user} }, @refs; + } + } + } + } + else + { + die "can't make head or tail of '$_'\n"; + } +} + +open(OUT, ">", $GL_CONF_COMPILED) + or die "open GL conf compiled failed: $!"; +print OUT Data::Dumper->Dump([\%repos], [qw(*repos)]); +close(OUT); + +# ---------------------------------------------------------------------------- +# any new repos created? +# ---------------------------------------------------------------------------- + +# modern gits allow cloning from an empty repo, so we just create it. Gitosis +# did not have that luxury, so it was forced to detect the first push and +# create it then + +umask(0077); +my_chdir("$ENV{HOME}/$REPO_BASE"); +for my $repo (keys %repos) +{ + unless (-d "$repo.git") + { + mkdir("$repo.git") or die "mkdir $repo.git failed: $!"; + my_chdir("$repo.git"); + system("git init --bare"); + system("cp $GL_ADMINDIR/update-hook.pl hooks/update"); + system("chmod 755 hooks/update"); + my_chdir("$ENV{HOME}/$REPO_BASE"); + } +} + # ---------------------------------------------------------------------------- # "compile" ssh authorized_keys # ---------------------------------------------------------------------------- @@ -144,95 +243,3 @@ if (-d ".git") close(COMMIT) or die "close commit failed: $!"; } } - -# ---------------------------------------------------------------------------- -# "compile" GL conf -# ---------------------------------------------------------------------------- - -open(INF, "<", $GL_CONF) - or die "open GL conf failed: $!"; -open(OUT, ">", $GL_CONF_COMPILED) - or die "open GL conf compiled failed: $!"; - -# the syntax is fairly simple, so we parse it inline - -my @repos; -while () -{ - # normalise whitespace; keeps later regexes very simple - s/=/ = /; - s/\s+/ /g; - s/^ //; - s/ $//; - # kill comments - s/#.*//; - # and blank lines - next unless /\S/; - - - # user groups - if (/^(@\S+) = (.*)/) - { - push @{ $groups{$1} }, expand_userlist( split(' ', $2) ); - } - # repo(s) - elsif (/^repo (.*)/) - { - @repos = split(' ', $1); - } - # actual permission line - elsif (/^(R|RW|RW\+) (.* )?= (.+)/) - { - my @perms = split //, $1; - my @refs; @refs = split(' ', $2) if $2; - my @users = split ' ', $3; - - # if no ref is given, this PERM applies to all refs - @refs = qw(refs/.*) unless @refs; - # fully qualify refs that dont start with "refs/"; prefix them with - # "refs/heads/" - @refs = map { m(^refs/) or s(^)(refs/heads/); $_ } @refs; - - # expand the user list, unless it is just "@all" - @users = expand_userlist ( @users ) - unless (@users == 1 and $users[0] eq '@all'); - - # ok, we can finally populate the %repos hash - for my $repo (@repos) # each repo in the current stanza - { - for my $perm (@perms) - { - for my $user (@users) - { - push @{ $repos{$repo}{$perm}{$user} }, @refs; - } - } - } - } -} - -print OUT Data::Dumper->Dump([\%repos], [qw(*repos)]); -close(OUT); - -# ---------------------------------------------------------------------------- -# any new repos created? -# ---------------------------------------------------------------------------- - -# modern gits allow cloning from an empty repo, so we just create it. Gitosis -# did not have that luxury, so it was forced to detect the first push and -# create it then - -umask(0077); -my_chdir("$ENV{HOME}/$REPO_BASE"); -for my $repo (keys %repos) -{ - unless (-d "$repo.git") - { - mkdir("$repo.git") or die "mkdir $repo.git failed: $!"; - my_chdir("$repo.git"); - system("git init --bare"); - system("cp $GL_ADMINDIR/update-hook.pl hooks/update"); - system("chmod 755 hooks/update"); - my_chdir("$ENV{HOME}/$REPO_BASE"); - } -} From 0f726bea9a8fe9d1548968398a4b108bd5835304 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 24 Aug 2009 20:30:26 +0530 Subject: [PATCH 015/850] added license info --- COPYING | 339 ++++++++++++++++++++++++++++++++++++++++++++++++ INSTALL | 4 + README.markdown | 3 +- 3 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/INSTALL b/INSTALL index ce0d1d0..e4350e3 100644 --- a/INSTALL +++ b/INSTALL @@ -97,3 +97,7 @@ And once in a while, if you're feeling particularly BOFH-ish, take a look at * when you clone an empty repo, git seems to complain about the remote hanging up or something. I have no idea what that is, but it doesn't seem to hurt anything. This happens even in normal git, not just gitosis-lite. + +---- + +gitosis-lite is released under the GPL v2 license. See COPYING for details diff --git a/README.markdown b/README.markdown index 363378e..ac4effe 100644 --- a/README.markdown +++ b/README.markdown @@ -2,7 +2,8 @@ gitosis-lite is the bare essentials of gitosis, with a completely different config file that allows (at last!) access control down to the branch level, -including specifying who can and cannot *rewind* a given branch. +including specifying who can and cannot *rewind* a given branch. It is +released under GPL v2. See COPYING for details. In this document: From 92cf77d9c2007c3354ae5c4e7d7fdefd7409cc32 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 25 Aug 2009 06:38:05 +0530 Subject: [PATCH 016/850] update hook: squelch message for branch deletion Ilari pointed out that in case of branch deletion the *new* SHA could be 0, which causes an ugly fatal: Not a valid commit name 0000000000000000000000000000000000000000 Since we consider deletion an extreme form of rewind, the end result does not change ($merge_base will be unequal to $oldsha anyway), but we do need to squelch the ugly message. --- update-hook.pl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/update-hook.pl b/update-hook.pl index b881f5a..8c6d917 100755 --- a/update-hook.pl +++ b/update-hook.pl @@ -55,17 +55,21 @@ my $ref = shift; my $oldsha = shift; my $newsha = shift; my $merge_base = '0' x 40; +# compute a merge-base if both SHAs are non-0, else leave it as '0'x40 +# (i.e., for branch create or delete, merge_base == '0'x40) chomp($merge_base = `git merge-base $oldsha $newsha`) - unless $oldsha eq '0' x 40; + unless $oldsha eq '0' x 40 + or $newsha eq '0' x 40; # some of this is from an example hook in Documentation/howto of git.git, with # some variations # what are you trying to do? (is it 'W' or '+'?) my $perm = 'W'; -# rewriting a tag +# rewriting a tag is considered a rewind, in terms of permissions $perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); -# non-ff push to branch +# non-ff push to branch. Notice that branch delete looks like a rewind, as it +# should $perm = '+' if $ref =~ m(refs/heads/) and $oldsha ne $merge_base; my $allowed_refs = $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}}; From 1f1b95f4c63879a1ebb921fe556ccc141333bc39 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 25 Aug 2009 07:06:36 +0530 Subject: [PATCH 017/850] compile: move umask up to cover other outputs also --- gl-compile-conf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gl-compile-conf b/gl-compile-conf index ac4134d..da3163a 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -66,6 +66,9 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple patter our %groups = (); our %repos = (); +# set a restrictive umask, just in case +umask(0077); + # ---------------------------------------------------------------------------- # subroutines # ---------------------------------------------------------------------------- @@ -180,7 +183,6 @@ close(OUT); # did not have that luxury, so it was forced to detect the first push and # create it then -umask(0077); my_chdir("$ENV{HOME}/$REPO_BASE"); for my $repo (keys %repos) { From ebf6300d01eee439d5d4708503d08b4159232aee Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 25 Aug 2009 09:08:11 +0530 Subject: [PATCH 018/850] all: some "our"s changed to "my" --- gl-auth-command | 6 +++--- gl-compile-conf | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gl-auth-command b/gl-auth-command index e1be7d7..c05f53c 100755 --- a/gl-auth-command +++ b/gl-auth-command @@ -43,9 +43,9 @@ die "couldnt do perms file" unless (my $ret = do $GL_CONF_COMPILED); # definitions specific to this program # ---------------------------------------------------------------------------- -our $R_COMMANDS=qr/git[ -]upload-pack/; -our $W_COMMANDS=qr/git[ -]receive-pack/; -our $REPONAME_PATT=qr(^[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern +my $R_COMMANDS=qr/git[ -]upload-pack/; +my $W_COMMANDS=qr/git[ -]receive-pack/; +my $REPONAME_PATT=qr(^[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern # ---------------------------------------------------------------------------- # start... diff --git a/gl-compile-conf b/gl-compile-conf index da3163a..b668228 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -59,12 +59,12 @@ unless (my $ret = do $glrc) # ---------------------------------------------------------------------------- # command and options for authorized_keys -our $AUTH_COMMAND="$GL_ADMINDIR/gl-auth-command"; -our $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; -our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern +my $AUTH_COMMAND="$GL_ADMINDIR/gl-auth-command"; +my $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; +my $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern -our %groups = (); -our %repos = (); +my %groups = (); +my %repos = (); # set a restrictive umask, just in case umask(0077); From 0b0d95a1ff88b0613996ca15745098e85453c6bd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 25 Aug 2009 09:21:07 +0530 Subject: [PATCH 019/850] auth: tighten up 2 regexes; one minor code clarity fix --- gl-auth-command | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gl-auth-command b/gl-auth-command index c05f53c..8d02d3d 100755 --- a/gl-auth-command +++ b/gl-auth-command @@ -43,8 +43,8 @@ die "couldnt do perms file" unless (my $ret = do $GL_CONF_COMPILED); # definitions specific to this program # ---------------------------------------------------------------------------- -my $R_COMMANDS=qr/git[ -]upload-pack/; -my $W_COMMANDS=qr/git[ -]receive-pack/; +my $R_COMMANDS=qr/^git[ -]upload-pack$/; +my $W_COMMANDS=qr/^git[ -]receive-pack$/; my $REPONAME_PATT=qr(^[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern # ---------------------------------------------------------------------------- @@ -87,8 +87,9 @@ die "I don't like the look of $repo, sorry!" # first level permissions check # ---------------------------------------------------------------------------- -# now, knowing the user and repo (which is repo path), we try perms -my $perm = 'W'; $perm = 'R' if $verb =~ $R_COMMANDS; +# we know the user and repo; we just need to know what perm he's trying +my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W'); + die "access denied" unless $repos{$repo}{$perm}{$user}; # ---------------------------------------------------------------------------- From 66bf4a20f9f171895ab7c12c6b2fec04e9e9371c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 25 Aug 2009 09:57:19 +0530 Subject: [PATCH 020/850] all: lexical file handles instead of bare --- gl-auth-command | 9 ++++++--- gl-compile-conf | 44 +++++++++++++++++++++++--------------------- update-hook.pl | 14 ++++++-------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/gl-auth-command b/gl-auth-command index 8d02d3d..eb809ee 100755 --- a/gl-auth-command +++ b/gl-auth-command @@ -99,9 +99,12 @@ die "access denied" unless $repos{$repo}{$perm}{$user}; # ( but first save the reponame; we can save some time later in the hook ) $ENV{GL_REPO}=$repo; -open(LOG, ">>", "$GL_ADMINDIR/log"); -print LOG "\n", scalar(localtime), " $ENV{SSH_ORIGINAL_COMMAND} $user\n"; -close(LOG); +# if log failure isn't important enough to block access, get rid of all the +# error checking +open my $log_fh, ">>", "$GL_ADMINDIR/log" + or die "open log failed: $!"; +print $log_fh "\n", scalar(localtime), " $ENV{SSH_ORIGINAL_COMMAND} $user\n"; +close $log_fh or die "close log failed: $!"; $repo = "'$REPO_BASE/$repo.git'"; exec("git", "shell", "-c", "$verb $repo"); diff --git a/gl-compile-conf b/gl-compile-conf index b668228..6568c0f 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -105,13 +105,13 @@ sub expand_userlist # "compile" GL conf # ---------------------------------------------------------------------------- -open(INF, "<", $GL_CONF) - or die "open GL conf failed: $!"; +open my $conf_fh, "<", $GL_CONF + or die "open conf failed: $!"; # the syntax is fairly simple, so we parse it inline my @repos; -while () +while (<$conf_fh>) { # normalise whitespace; keeps later regexes very simple s/=/ = /; @@ -170,10 +170,10 @@ while () } } -open(OUT, ">", $GL_CONF_COMPILED) - or die "open GL conf compiled failed: $!"; -print OUT Data::Dumper->Dump([\%repos], [qw(*repos)]); -close(OUT); +open my $compiled_fh, ">", $GL_CONF_COMPILED + or die "open compiled-conf failed: $!"; +print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]); +close $compiled_fh or die "close compiled-conf failed: $!"; # ---------------------------------------------------------------------------- # any new repos created? @@ -201,26 +201,28 @@ for my $repo (keys %repos) # "compile" ssh authorized_keys # ---------------------------------------------------------------------------- -open(INF, "<", $ENV{HOME} . "/.ssh/authorized_keys") or die "open old authkeys failed: $!"; -open(OUT, ">", $ENV{HOME} . "/.ssh/new_authkeys") or die "open new authkeys failed: $!"; +open my $authkeys_fh, "<", $ENV{HOME} . "/.ssh/authorized_keys" + or die "open authkeys failed: $!"; +open my $newkeys_fh, ">", $ENV{HOME} . "/.ssh/new_authkeys" + or die "open newkeys failed: $!"; # save existing authkeys minus the GL-added stuff -while () +while (<$authkeys_fh>) { - print OUT unless (/^# gitosis-lite start/../^# gitosis-lite end/); + print $newkeys_fh unless (/^# gitosis-lite start/../^# gitosis-lite end/); } # add our "start" line, each key on its own line (prefixed by command and # options, in the standard ssh authorized_keys format), then the "end" line. -print OUT "# gitosis-lite start\n"; +print $newkeys_fh "# gitosis-lite start\n"; my_chdir($GL_KEYDIR); for my $pubkey (glob("*.pub")) { my $user = $pubkey; $user =~ s/\.pub$//; - print OUT "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; - print OUT `cat $pubkey`; + print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; + print $newkeys_fh `cat $pubkey`; } -print OUT "# gitosis-lite end\n"; -close(OUT); +print $newkeys_fh "# gitosis-lite end\n"; +close $newkeys_fh or die "close newkeys failed: $!"; # check what changes are being made; just a comfort factor # system("vim -d ~/.ssh/authorized_keys ~/.ssh/new_authkeys"); @@ -238,10 +240,10 @@ if (-d ".git") # and if there are any if (system("git diff --cached --quiet") ) { - open(COMMIT, "|-", "git commit -F -") - or die "pipe commit failed: $!"; - print COMMIT "keydir changed\n\n"; - print COMMIT `git diff --cached --name-status`; - close(COMMIT) or die "close commit failed: $!"; + open my $commit_ph, "|-", "git commit -F -" + or die "open commit failed: $!"; + print $commit_ph "keydir changed\n\n"; + print $commit_ph `git diff --cached --name-status`; + close $commit_ph or die "close commit failed: $!"; } } diff --git a/update-hook.pl b/update-hook.pl index 8c6d917..babd005 100755 --- a/update-hook.pl +++ b/update-hook.pl @@ -41,12 +41,6 @@ unless (my $ret = do $glrc) die "couldnt do perms file" unless (my $ret = do $GL_CONF_COMPILED); -# ---------------------------------------------------------------------------- -# definitions specific to this program -# ---------------------------------------------------------------------------- - -open(LOG, ">>", "$GL_ADMINDIR/log"); - # ---------------------------------------------------------------------------- # start... # ---------------------------------------------------------------------------- @@ -78,8 +72,12 @@ for my $refex (@$allowed_refs) { if ($ref =~ /$refex/) { - print LOG "$perm: $ENV{GL_USER} $ENV{GL_REPO} $ref $oldsha $newsha\n"; - close (LOG); + # if log failure isn't important enough to block pushes, get rid of + # all the error checking + open my $log_fh, ">>", "$GL_ADMINDIR/log" + or die "open log failed: $!"; + print $log_fh "$perm: $ENV{GL_USER} $ENV{GL_REPO} $ref $oldsha $newsha\n"; + close $log_fh or die "close log failed: $!"; exit 0; } } From 33963391a5bd7c3e5e3751922c264c34b94918a0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 25 Aug 2009 20:08:47 +0530 Subject: [PATCH 021/850] INSTALL: pre-requisites, and lots of stuff on ssh the duplicate pubkey problem and the need to document it was highlighted by Ilari ...damn I spend more time on ssh than on git it seems to me :( --- INSTALL | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/INSTALL b/INSTALL index e4350e3..bfbea1e 100644 --- a/INSTALL +++ b/INSTALL @@ -1,3 +1,17 @@ +### pre-requisites + +One of the big needs I'm trying to fill here is people who do not have root +access, permissions to create other userids, etc. This could be a typical +hosting provider type of thing, or -- in a corporate setting -- a very tightly +controlled server. + +Gitosis-lite requires these: + + * git itself, the more recent the better + * perl, typically installed with git, since git sort of needs it; any + version that includes `Data::Dumper`[1] will do. + * one user account on the server, with password access [2] + ### quickinstall I assume all the files pertaining to this software are untarred and available @@ -100,4 +114,41 @@ And once in a while, if you're feeling particularly BOFH-ish, take a look at ---- +Footnotes: + +[1] Actually, due to the way gitosis-lite is architected, you can manage +without `Data::Dumper` on the server if you have no choice. Only +`gl-compile-conf` needs it, so just run that on some other machine and copy +the two output files across. Cumbersome but doable... the advantage of +separating all the hard work into a manually-run piece :) + +[2] If you have *only* pubkey access, and **no** password access, then your +pubkey is already in the server's `~/.ssh/authorized_keys`. If you also need +to access git as a developer (clone, push, etc), do *not* submit this same +pubkey to gitosis-lite -- it won't work. + +Instead, create a different keypair for your "developer" role (by, e.g., +`ssh-keygen -t rsa -f ~/.ssh/gitdev`), then give `~/.ssh/gitdev.pub` to +gitosis-lite as "yourname.pub", just like you would do for any other user. + +Then you create a suitable `~/.ssh/config` to use the correct key +automatically, something like this: + + host gitadm + hostname my.server + user my_userid_on_server + + host gitdev + hostname my.server + user my_userid_on_server + identityfile ~/.ssh/gitdev + +From now on, `ssh gitadm` will get you a command line on the server, to do +gitosis-lite admin and other work. And your repository URLs would look like +`gitdev:reponame.git`. Very, very, simple... + +And as with gitosis, there's more "ssh" magic than "git" magic here :-) + +---- + gitosis-lite is released under the GPL v2 license. See COPYING for details From 7a9bf9c2960e557cea031e7adc306c2131cfeea2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 25 Aug 2009 20:11:44 +0530 Subject: [PATCH 022/850] conf-convert: quick and dirty hack, works fine :) --- conf-convert.pl | 101 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100755 conf-convert.pl diff --git a/conf-convert.pl b/conf-convert.pl new file mode 100755 index 0000000..4f8e913 --- /dev/null +++ b/conf-convert.pl @@ -0,0 +1,101 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +# migrate gitosis.conf to gitosis-lite.conf format + +# not very smart, but there shouldn't be any errors for simple configurations. +# the biggest thing you'll find is probably some comments rearranged or +# something, due to the "flush" thing below + +# for stuff it can't handle, it'll ignore the trivial ones (like gitweb and +# daemon), and put in an obviously syntax error-ed line for "repositories" and +# "map" statements. + +my @repos; +my @RO_repos; +my @comments; +my @users; +my $groupname; + +# a gitosis.conf stanza ends when a new "[group name]" line shows up, so you +# can't write as you go; you have to accumulate and flush +sub flush { + die "repos but no users?" if (not @users and (@repos or @RO_repos)); + # just a groupname + if (@users and not (@repos or @RO_repos)) { + print "\@$groupname = ", join(" ", @users), "\n"; + } + # RW repos + if (@repos) + { + print "repo ", join(" ", @repos), "\n"; + print " RW = ", join(" ", @users), "\n"; + } + # RO repos + if (@RO_repos) + { + print "repo ", join(" ", @RO_repos), "\n"; + print " R = ", join(" ", @users), "\n"; + } + # comments; yes there'll be some reordering, sorry! + print @comments if @comments; + + # empty out for next round + @users = (); + @repos = (); + @RO_repos = (); + @comments = (); +} + +while (<>) +{ + # pure comment lines or blank lines + if (/^\s*#/ or /^\s*$/) { + push @comments, $_; + next; + } + + # not supported + if (/^repositories *=/ or /^map /) { + print STDERR "not supported: $_"; + s/^/NOT SUPPORTED: /; + print; + next; + } + + chomp; + + # normalise whitespace to help later regexes + s/\s+/ /g; + s/ ?= ?/ = /; + s/^ //; + s/ $//; + + # the chaff... + next if /^\[(gitosis|repo)\]$/ + or /^(gitweb|daemon|loglevel|description|owner) =/; + + # the wheat... + if (/^members = (.*)/) { + push @users, split(' ', $1); + next; + } + if (/^write?able = (.*)/) { + push @repos, split(' ', $1); + next; + } + if (/^readonly = (.*)/) { + push @RO_repos, split(' ', $1); + next; + } + + # new group starts + if (/^\[group (.*?) ?\]/) { + flush(); + $groupname = $1; + } +} + +flush(); From cb5a802d3e858672fe700574e9b1c02793d85cd1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 25 Aug 2009 21:47:12 +0530 Subject: [PATCH 023/850] DAMN DAMN DAMN those lexical filehandles print FH unless () # was working fine but print $fh unless () # doesn't instead of printing $_ to $fh, it prints $fh to STDOUT. DAMN DAMN DAMN --- gl-compile-conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gl-compile-conf b/gl-compile-conf index 6568c0f..19cdbb5 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -208,7 +208,7 @@ open my $newkeys_fh, ">", $ENV{HOME} . "/.ssh/new_authkeys" # save existing authkeys minus the GL-added stuff while (<$authkeys_fh>) { - print $newkeys_fh unless (/^# gitosis-lite start/../^# gitosis-lite end/); + print $newkeys_fh $_ unless (/^# gitosis-lite start/../^# gitosis-lite end/); } # add our "start" line, each key on its own line (prefixed by command and From 09aeb311984202745e7b2fc8138b2b30a05d947d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 26 Aug 2009 06:17:27 +0530 Subject: [PATCH 024/850] project renamed to gitolite --- INSTALL | 38 +++++++++---------- README.markdown | 4 +- conf-convert.pl | 2 +- example.conf | 2 +- ...ple.gitosis-lite.rc => example.gitolite.rc | 8 ++-- gl-auth-command | 4 +- gl-compile-conf | 20 +++++----- update-hook.pl | 6 +-- 8 files changed, 42 insertions(+), 42 deletions(-) rename example.gitosis-lite.rc => example.gitolite.rc (67%) diff --git a/INSTALL b/INSTALL index bfbea1e..c815405 100644 --- a/INSTALL +++ b/INSTALL @@ -5,7 +5,7 @@ access, permissions to create other userids, etc. This could be a typical hosting provider type of thing, or -- in a corporate setting -- a very tightly controlled server. -Gitosis-lite requires these: +Gitolite requires these: * git itself, the more recent the better * perl, typically installed with git, since git sort of needs it; any @@ -21,31 +21,31 @@ A quick install, taking all the defaults, can be done with the following commands; just copy and paste them into your shell: # this one is fixed to the location shown - cp example.gitosis-lite.rc ~/.gitosis-lite.rc + cp example.gitolite.rc ~/.gitolite.rc # the destinations below are defaults; if you change the paths in the "rc" # file above, these destinations also must change accordingly # mkdir $REPO_BASE, $GL_ADMINDIR, and $GL_KEYDIR mkdir ~/repositories - mkdir ~/.gitosis-lite - mkdir ~/.gitosis-lite/keydir + mkdir ~/.gitolite + mkdir ~/.gitolite/keydir # copy sample conf to $GL_CONF - cp example.conf ~/.gitosis-lite/gitosis-lite.conf + cp example.conf ~/.gitolite/gitolite.conf # copy the 3 programs to $GL_ADMINDIR - cp update-hook.pl ~/.gitosis-lite - cp gl-auth-command ~/.gitosis-lite - cp gl-compile-conf ~/.gitosis-lite + cp update-hook.pl ~/.gitolite + cp gl-auth-command ~/.gitolite + cp gl-compile-conf ~/.gitolite # optional; copy the documents also (if you untarred the package into a # temporary directory and need to get rid of it) - cp INSTALL README.markdown ~/.gitosis-lite + cp INSTALL README.markdown ~/.gitolite ### install notes - * At present the location of `~/.gitosis-lite.rc` is fixed (maybe later I'll + * At present the location of `~/.gitolite.rc` is fixed (maybe later I'll change it to a "git config" variable). If you edit it and change any paths, be sure to keep the perl syntax -- @@ -53,11 +53,11 @@ commands; just copy and paste them into your shell: limited case. And of course, make sure you adjust the commands shown above to suit the new locations - * the config file is (by default) at `~/.gitosis-lite/gitosis-lite.conf`. + * the config file is (by default) at `~/.gitolite/gitolite.conf`. Edit the file as you wish. The comments in the file ought to be clear enough but let me know if not - * if you want to bring in existing (bare, server) repos into gitosis-lite, + * if you want to bring in existing (bare, server) repos into gitolite, this should work: * backup the repo, then move it to `$BASE_REPO` * copy `$GL_ADMINDIR/update-hook.pl` to `[reponame].git/hooks/update` -- @@ -89,7 +89,7 @@ It should all work, but the first couple of times you may want to check these `$GL_ADMINDIR/gl-auth-command` file, then some sshd restrictions, the key, etc. * `$GL_CONF_COMPILED` (default - `~/.gitosis-lite/gitosis-lite.conf-compiled.pm`) should contain an + `~/.gitolite/gitolite.conf-compiled.pm`) should contain an expanded list of the access control rules. It may look a little long, but it's fairly intuitive! @@ -110,13 +110,13 @@ And once in a while, if you're feeling particularly BOFH-ish, take a look at * when you clone an empty repo, git seems to complain about the remote hanging up or something. I have no idea what that is, but it doesn't seem - to hurt anything. This happens even in normal git, not just gitosis-lite. + to hurt anything. This happens even in normal git, not just gitolite. ---- Footnotes: -[1] Actually, due to the way gitosis-lite is architected, you can manage +[1] Actually, due to the way gitolite is architected, you can manage without `Data::Dumper` on the server if you have no choice. Only `gl-compile-conf` needs it, so just run that on some other machine and copy the two output files across. Cumbersome but doable... the advantage of @@ -125,11 +125,11 @@ separating all the hard work into a manually-run piece :) [2] If you have *only* pubkey access, and **no** password access, then your pubkey is already in the server's `~/.ssh/authorized_keys`. If you also need to access git as a developer (clone, push, etc), do *not* submit this same -pubkey to gitosis-lite -- it won't work. +pubkey to gitolite -- it won't work. Instead, create a different keypair for your "developer" role (by, e.g., `ssh-keygen -t rsa -f ~/.ssh/gitdev`), then give `~/.ssh/gitdev.pub` to -gitosis-lite as "yourname.pub", just like you would do for any other user. +gitolite as "yourname.pub", just like you would do for any other user. Then you create a suitable `~/.ssh/config` to use the correct key automatically, something like this: @@ -144,11 +144,11 @@ automatically, something like this: identityfile ~/.ssh/gitdev From now on, `ssh gitadm` will get you a command line on the server, to do -gitosis-lite admin and other work. And your repository URLs would look like +gitolite admin and other work. And your repository URLs would look like `gitdev:reponame.git`. Very, very, simple... And as with gitosis, there's more "ssh" magic than "git" magic here :-) ---- -gitosis-lite is released under the GPL v2 license. See COPYING for details +gitolite is released under the GPL v2 license. See COPYING for details diff --git a/README.markdown b/README.markdown index ac4effe..e8349ed 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,6 @@ -# gitosis-lite +# gitolite -gitosis-lite is the bare essentials of gitosis, with a completely different +Gitolite is the bare essentials of gitosis, with a completely different config file that allows (at last!) access control down to the branch level, including specifying who can and cannot *rewind* a given branch. It is released under GPL v2. See COPYING for details. diff --git a/conf-convert.pl b/conf-convert.pl index 4f8e913..f9775cb 100755 --- a/conf-convert.pl +++ b/conf-convert.pl @@ -3,7 +3,7 @@ use strict; use warnings; -# migrate gitosis.conf to gitosis-lite.conf format +# migrate gitosis.conf to gitolite.conf format # not very smart, but there shouldn't be any errors for simple configurations. # the biggest thing you'll find is probably some comments rearranged or diff --git a/example.conf b/example.conf index 89f3bd0..f19a95a 100644 --- a/example.conf +++ b/example.conf @@ -1,4 +1,4 @@ -# example conf file for gitosis-lite +# example conf file for gitolite # overall syntax: # - everything in this is space-separated; no commas, semicolons, etc diff --git a/example.gitosis-lite.rc b/example.gitolite.rc similarity index 67% rename from example.gitosis-lite.rc rename to example.gitolite.rc index ddbcca8..72ad3dc 100644 --- a/example.gitosis-lite.rc +++ b/example.gitolite.rc @@ -3,17 +3,17 @@ # base directory for all the repos $REPO_BASE="repositories"; -# gitosis-lite admin directory, files, etc -$GL_ADMINDIR=$ENV{HOME} . "/.gitosis-lite"; +# gitolite admin directory, files, etc +$GL_ADMINDIR=$ENV{HOME} . "/.gitolite"; # -------------------------------------- # the ones below can be left as they are, unless for some reason you want them # elsewhere -$GL_CONF="$GL_ADMINDIR/gitosis-lite.conf"; +$GL_CONF="$GL_ADMINDIR/gitolite.conf"; $GL_KEYDIR="$GL_ADMINDIR/keydir"; -$GL_CONF_COMPILED="$GL_ADMINDIR/gitosis-lite.conf-compiled.pm"; +$GL_CONF_COMPILED="$GL_ADMINDIR/gitolite.conf-compiled.pm"; # -------------------------------------- # this should be the last line in this file, per perl rules diff --git a/gl-auth-command b/gl-auth-command index eb809ee..ffb4060 100755 --- a/gl-auth-command +++ b/gl-auth-command @@ -5,7 +5,7 @@ use strict; # === auth-command === # the command that GL users actually run -# part of the gitosis-lite (GL) suite +# part of the gitolite (GL) suite # how run: via sshd, being listed in "command=" in ssh authkeys # when: every login by a GL user @@ -29,7 +29,7 @@ our $GL_CONF_COMPILED; our $REPO_BASE; our %repos; -my $glrc = $ENV{HOME} . "/.gitosis-lite.rc"; +my $glrc = $ENV{HOME} . "/.gitolite.rc"; unless (my $ret = do $glrc) { die "parse $glrc failed: $@" if $@; diff --git a/gl-compile-conf b/gl-compile-conf index 19cdbb5..a3ae0d0 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -5,7 +5,7 @@ use Data::Dumper; # === add-auth-keys === -# part of the gitosis-lite (GL) suite +# part of the gitolite (GL) suite # (1) - "compiles" ~/.ssh/authorized_keys from the list of pub-keys # (2) - also "compiles" the user-friendly GL conf file into something easier @@ -17,13 +17,13 @@ use Data::Dumper; # how run: manual, by GL admin # when: # - anytime a pubkey is added/deleted -# - anytime gitosis-lite.conf is changed +# - anytime gitolite.conf is changed # input: -# - GL_CONF (default: ~/.gitosis-lite/gitosis-lite.conf) -# - GL_KEYDIR (default: ~/.gitosis-lite/keydir) +# - GL_CONF (default: ~/.gitolite/gitolite.conf) +# - GL_KEYDIR (default: ~/.gitolite/keydir) # output: # - ~/.ssh/authorized_keys (dictated by sshd) -# - GL_CONF_COMPILED (default: ~/.gitosis-lite/gitosis-lite.conf-compiled.pm) +# - GL_CONF_COMPILED (default: ~/.gitolite/gitolite.conf-compiled.pm) # security: # - touches a very critical system file that manages the restrictions on # incoming users. Be sure to audit AUTH_COMMAND and AUTH_OPTIONS (see @@ -46,7 +46,7 @@ our $GL_KEYDIR; our $GL_CONF_COMPILED; our $REPO_BASE; -my $glrc = $ENV{HOME} . "/.gitosis-lite.rc"; +my $glrc = $ENV{HOME} . "/.gitolite.rc"; unless (my $ret = do $glrc) { die "parse $glrc failed: $@" if $@; @@ -208,12 +208,12 @@ open my $newkeys_fh, ">", $ENV{HOME} . "/.ssh/new_authkeys" # save existing authkeys minus the GL-added stuff while (<$authkeys_fh>) { - print $newkeys_fh $_ unless (/^# gitosis-lite start/../^# gitosis-lite end/); + print $newkeys_fh $_ unless (/^# gitolite start/../^# gitolite end/); } # add our "start" line, each key on its own line (prefixed by command and # options, in the standard ssh authorized_keys format), then the "end" line. -print $newkeys_fh "# gitosis-lite start\n"; +print $newkeys_fh "# gitolite start\n"; my_chdir($GL_KEYDIR); for my $pubkey (glob("*.pub")) { @@ -221,7 +221,7 @@ for my $pubkey (glob("*.pub")) print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; print $newkeys_fh `cat $pubkey`; } -print $newkeys_fh "# gitosis-lite end\n"; +print $newkeys_fh "# gitolite end\n"; close $newkeys_fh or die "close newkeys failed: $!"; # check what changes are being made; just a comfort factor @@ -231,7 +231,7 @@ close $newkeys_fh or die "close newkeys failed: $!"; system("cat ~/.ssh/new_authkeys > ~/.ssh/authorized_keys"); system("rm ~/.ssh/new_authkeys"); -# if the gl admin directory (~/.gitosis-lite) is itself a git repo, do an +# if the gl admin directory (~/.gitolite) is itself a git repo, do an # autocheckin. nothing fancy; this is a "just in case" type of thing. my_chdir($GL_ADMINDIR); if (-d ".git") diff --git a/update-hook.pl b/update-hook.pl index babd005..2883a67 100755 --- a/update-hook.pl +++ b/update-hook.pl @@ -3,9 +3,9 @@ use strict; # === update === -# this is gitosis-lite's update hook +# this is gitolite's update hook -# part of the gitosis-lite (GL) suite +# part of the gitolite (GL) suite # how run: via git, being copied as .git/hooks/update in every repo # when: every push @@ -31,7 +31,7 @@ our $GL_CONF_COMPILED; our $REPO_BASE; our %repos; -my $glrc = $ENV{HOME} . "/.gitosis-lite.rc"; +my $glrc = $ENV{HOME} . "/.gitolite.rc"; unless (my $ret = do $glrc) { die "parse $glrc failed: $@" if $@; From dc4f32b14e94bc8a03d6f552c345ba93370ca48a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 26 Aug 2009 07:05:04 +0530 Subject: [PATCH 025/850] backward compat for marker lines in authkeys --- gl-compile-conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gl-compile-conf b/gl-compile-conf index a3ae0d0..76c4239 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -208,7 +208,7 @@ open my $newkeys_fh, ">", $ENV{HOME} . "/.ssh/new_authkeys" # save existing authkeys minus the GL-added stuff while (<$authkeys_fh>) { - print $newkeys_fh $_ unless (/^# gitolite start/../^# gitolite end/); + print $newkeys_fh $_ unless (/^# gito(sis-)?lite start/../^# gito(sis-)?lite end/); } # add our "start" line, each key on its own line (prefixed by command and From 0a32d46f5951ba9b2d6efbdbda0eea6c788086e3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 25 Aug 2009 08:44:46 +0530 Subject: [PATCH 026/850] use-warnings --- gl-auth-command | 3 ++- gl-compile-conf | 3 ++- update-hook.pl | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gl-auth-command b/gl-auth-command index ffb4060..cd3f450 100755 --- a/gl-auth-command +++ b/gl-auth-command @@ -1,6 +1,7 @@ -#!/usr/bin/perl -w +#!/usr/bin/perl use strict; +use warnings; # === auth-command === # the command that GL users actually run diff --git a/gl-compile-conf b/gl-compile-conf index 76c4239..40d8088 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -1,6 +1,7 @@ -#!/usr/bin/perl -w +#!/usr/bin/perl use strict; +use warnings; use Data::Dumper; # === add-auth-keys === diff --git a/update-hook.pl b/update-hook.pl index 2883a67..2e98911 100755 --- a/update-hook.pl +++ b/update-hook.pl @@ -1,6 +1,7 @@ -#!/usr/bin/perl -w +#!/usr/bin/perl use strict; +use warnings; # === update === # this is gitolite's update hook From 43b658660db344ab85aee3c9d25503ae3dfafd86 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 26 Aug 2009 21:14:25 +0530 Subject: [PATCH 027/850] add ".mkd" to docs so gh will render them nicely, plus a couple of minor fixes --- INSTALL => INSTALL.mkd | 4 +--- README.markdown => README.mkd | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) rename INSTALL => INSTALL.mkd (99%) rename README.markdown => README.mkd (99%) diff --git a/INSTALL b/INSTALL.mkd similarity index 99% rename from INSTALL rename to INSTALL.mkd index c815405..402cc89 100644 --- a/INSTALL +++ b/INSTALL.mkd @@ -112,9 +112,7 @@ And once in a while, if you're feeling particularly BOFH-ish, take a look at hanging up or something. I have no idea what that is, but it doesn't seem to hurt anything. This happens even in normal git, not just gitolite. ----- - -Footnotes: +### Footnotes: [1] Actually, due to the way gitolite is architected, you can manage without `Data::Dumper` on the server if you have no choice. Only diff --git a/README.markdown b/README.mkd similarity index 99% rename from README.markdown rename to README.mkd index e8349ed..e7d557e 100644 --- a/README.markdown +++ b/README.mkd @@ -39,7 +39,7 @@ that: All of this pointed to a rewrite. In perl, naturally :-) -### what's extra? +### what's new Per-branch permissions. You will not believe how often I am asked this at $DAYJOB. This is almost the single reason I started *thinking* about rolling From 522b35434e2482dd6c7a0671bd9c4d848a464efb Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 27 Aug 2009 05:45:48 +0530 Subject: [PATCH 028/850] compile/INSTALL: multi-key feature code+doc --- INSTALL.mkd | 39 ++++++++++++++++++++++++++++++++++++++- gl-compile-conf | 2 +- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/INSTALL.mkd b/INSTALL.mkd index 402cc89..0df9894 100644 --- a/INSTALL.mkd +++ b/INSTALL.mkd @@ -1,3 +1,15 @@ +In this document: + + * pre-requisites + * quickinstall + * install notes + * administer + * run + * special cases + * errors, warnings, etc + +---- + ### pre-requisites One of the big needs I'm trying to fill here is people who do not have root @@ -71,7 +83,8 @@ commands; just copy and paste them into your shell: [here](http://sitaramc.github.com/0-installing/2-access-gitosis.html#generating_a_public_key)) for how to do this * for each "user" in `$GL_CONF`, copy their public key to a file called - "user.pub" in `$GL_KEYDIR` + "user.pub" in `$GL_KEYDIR`. For example, mine would be called + "sitaram.pub" * edit the config file (`$GL_CONF`) to add the new users in whatever way you like * backup your `~/.ssh/authorized_keys` file if you feel nervous :-) @@ -106,6 +119,30 @@ Just use it as normal. Every new repo mentioned has been created already, so And once in a while, if you're feeling particularly BOFH-ish, take a look at `$GL_ADMINDIR/log` :-) +### special cases + +#### one user, many keys + +Sometimes the same user needs to access the server from differnt machines +(like a desktop and a laptop, for instance). Gitolite needs to be given all +these public keys, but associate *all* of them with the same user. + +Recall from the "administer" section above that each "user" has one public key +file called "user.pub", which seems to imply a one-to-one match. + +But this is not strictly true -- gitolite allows a *filename* to have a small +"location" piece attached to it. So you can have "sitaram@laptop.pub" and +"sitaram@desktop.pub", for instance, and they'll all be treated as keys for +"sitaram". Just add both the files to "keydir/", and use the username +"sitaram" (*without* the "@location" part) in your `gitolite.conf` file. + +Advantages: if a user reports *one of his keys* is lost or needs replacing, +it's easy to remove or replace just that. + +(Gitosis keeps multiple entries in the same "user.pub", which means to delete +or change one of the keys you have to edit the file and figure out which of +the 2 or more long lines should be removed). + ### errors, warnings, etc * when you clone an empty repo, git seems to complain about the remote diff --git a/gl-compile-conf b/gl-compile-conf index 40d8088..78cc671 100755 --- a/gl-compile-conf +++ b/gl-compile-conf @@ -218,7 +218,7 @@ print $newkeys_fh "# gitolite start\n"; my_chdir($GL_KEYDIR); for my $pubkey (glob("*.pub")) { - my $user = $pubkey; $user =~ s/\.pub$//; + my $user = $pubkey; $user =~ s/(\@.+)?\.pub$//; print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; print $newkeys_fh `cat $pubkey`; } From 3ddc9087d3ead1561a6012bac309cbd84d8a48d6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 27 Aug 2009 13:14:47 +0530 Subject: [PATCH 029/850] first production use: @all, leading slash I had to make two minor fixes while migrating my work repos: 1. I forgot to honor '@all'; oops! While I was about it, I also fixed the "access denied" message to show what rights were being tried when it failed. 2. I forgot that URLs can have leading slashes (I myself only use URLs like gs:reponame.git, where gs is an ssh stanza that describes the git server in question). --- gl-auth-command | 11 +++++++---- update-hook.pl | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/gl-auth-command b/gl-auth-command index cd3f450..1fd294c 100755 --- a/gl-auth-command +++ b/gl-auth-command @@ -65,9 +65,10 @@ my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! my $cmd = $ENV{SSH_ORIGINAL_COMMAND} or die "no SSH_ORIGINAL_COMMAND? I'm not a shell, $user!"; -# we don't like newlines or semicolons in SSH_ORIGINAL_COMMAND +# this check is largely for comic value if someone tries something outrageous; +# $cmd gets split and the pieces examined more thoroughly later anyway die "$cmd??? you're a funny guy..." - if $cmd =~ /[;\n]/; + if $cmd =~ /[<>&|;\n]/; # split into command and arguments; the pattern allows old style as well as # new style: "git-subcommand arg" or "git subcommand arg", just like gitosis @@ -77,7 +78,7 @@ die "$cmd??? you're a funny guy..." # git-receive-pack 'reponame.git' # including the single quotes -my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'(.*).git'/); +my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*).git'/); die "$verb? I don't do odd jobs, sorry..." unless $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS; @@ -91,7 +92,9 @@ die "I don't like the look of $repo, sorry!" # we know the user and repo; we just need to know what perm he's trying my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W'); -die "access denied" unless $repos{$repo}{$perm}{$user}; +die "$perm access for $repo denied to $user" + unless $repos{$repo}{$perm}{$user} + or $repos{$repo}{$perm}{'@all'}; # ---------------------------------------------------------------------------- # over to git now diff --git a/update-hook.pl b/update-hook.pl index 2e98911..709266e 100755 --- a/update-hook.pl +++ b/update-hook.pl @@ -67,8 +67,10 @@ $perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); # should $perm = '+' if $ref =~ m(refs/heads/) and $oldsha ne $merge_base; -my $allowed_refs = $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}}; -for my $refex (@$allowed_refs) +my @allowed_refs; +push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}} }; +push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{'@all'} }; +for my $refex (@allowed_refs) # refex? sure -- a regex to match a ref against :) { if ($ref =~ /$refex/) From f0099a125eafefc551b1caba07df7e393eabd757 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 27 Aug 2009 14:00:00 +0530 Subject: [PATCH 030/850] reduce clutter by making src, doc, conf subdirectories --- example.conf => conf/example.conf | 0 example.gitolite.rc => conf/example.gitolite.rc | 0 COPYING => doc/COPYING | 0 INSTALL.mkd => doc/INSTALL.mkd | 0 TODO => doc/TODO | 0 conf-convert.pl => src/conf-convert.pl | 0 gl-auth-command => src/gl-auth-command | 0 gl-compile-conf => src/gl-compile-conf | 0 update-hook.pl => src/update-hook.pl | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename example.conf => conf/example.conf (100%) rename example.gitolite.rc => conf/example.gitolite.rc (100%) rename COPYING => doc/COPYING (100%) rename INSTALL.mkd => doc/INSTALL.mkd (100%) rename TODO => doc/TODO (100%) rename conf-convert.pl => src/conf-convert.pl (100%) rename gl-auth-command => src/gl-auth-command (100%) rename gl-compile-conf => src/gl-compile-conf (100%) rename update-hook.pl => src/update-hook.pl (100%) diff --git a/example.conf b/conf/example.conf similarity index 100% rename from example.conf rename to conf/example.conf diff --git a/example.gitolite.rc b/conf/example.gitolite.rc similarity index 100% rename from example.gitolite.rc rename to conf/example.gitolite.rc diff --git a/COPYING b/doc/COPYING similarity index 100% rename from COPYING rename to doc/COPYING diff --git a/INSTALL.mkd b/doc/INSTALL.mkd similarity index 100% rename from INSTALL.mkd rename to doc/INSTALL.mkd diff --git a/TODO b/doc/TODO similarity index 100% rename from TODO rename to doc/TODO diff --git a/conf-convert.pl b/src/conf-convert.pl similarity index 100% rename from conf-convert.pl rename to src/conf-convert.pl diff --git a/gl-auth-command b/src/gl-auth-command similarity index 100% rename from gl-auth-command rename to src/gl-auth-command diff --git a/gl-compile-conf b/src/gl-compile-conf similarity index 100% rename from gl-compile-conf rename to src/gl-compile-conf diff --git a/update-hook.pl b/src/update-hook.pl similarity index 100% rename from update-hook.pl rename to src/update-hook.pl From 4e74652b38fa4569c5533b4dbe4693c95a82f6cd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 27 Aug 2009 15:24:08 +0530 Subject: [PATCH 031/850] source code changes after splitting into src/doc/conf --- conf/example.gitolite.rc | 4 ++-- src/gl-compile-conf | 6 +++--- src/install.sh | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100755 src/install.sh diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 72ad3dc..7a5068d 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -11,9 +11,9 @@ $GL_ADMINDIR=$ENV{HOME} . "/.gitolite"; # the ones below can be left as they are, unless for some reason you want them # elsewhere -$GL_CONF="$GL_ADMINDIR/gitolite.conf"; +$GL_CONF="$GL_ADMINDIR/conf/gitolite.conf"; $GL_KEYDIR="$GL_ADMINDIR/keydir"; -$GL_CONF_COMPILED="$GL_ADMINDIR/gitolite.conf-compiled.pm"; +$GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm"; # -------------------------------------- # this should be the last line in this file, per perl rules diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 78cc671..3388979 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -60,7 +60,7 @@ unless (my $ret = do $glrc) # ---------------------------------------------------------------------------- # command and options for authorized_keys -my $AUTH_COMMAND="$GL_ADMINDIR/gl-auth-command"; +my $AUTH_COMMAND="$GL_ADMINDIR/src/gl-auth-command"; my $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; my $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern @@ -192,7 +192,7 @@ for my $repo (keys %repos) mkdir("$repo.git") or die "mkdir $repo.git failed: $!"; my_chdir("$repo.git"); system("git init --bare"); - system("cp $GL_ADMINDIR/update-hook.pl hooks/update"); + system("cp $GL_ADMINDIR/src/update-hook.pl hooks/update"); system("chmod 755 hooks/update"); my_chdir("$ENV{HOME}/$REPO_BASE"); } @@ -237,7 +237,7 @@ system("rm ~/.ssh/new_authkeys"); my_chdir($GL_ADMINDIR); if (-d ".git") { - system("git add -A keydir"); # stage all changes in keydir + system("git add -A conf keydir"); # stage all operational data # and if there are any if (system("git diff --cached --quiet") ) { diff --git a/src/install.sh b/src/install.sh new file mode 100755 index 0000000..c2156da --- /dev/null +++ b/src/install.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# install gitolite + +# quick safety check: do not run if ~/.gitolite.rc is present + +[[ -f ~/.gitolite.rc ]] && { + echo sorry\; \'~/.gitolite.rc\' already exists + exit 1 +} + +# this one is fixed to the location shown +cp conf/example.gitolite.rc ~/.gitolite.rc + +# the destinations below are defaults; if you change the paths in the "rc" +# file above, these destinations also must change accordingly + +# mkdir $REPO_BASE, $GL_ADMINDIR, it's subdirs, and $GL_KEYDIR +mkdir ~/repositories +mkdir ~/.gitolite +mkdir ~/.gitolite/{src,conf,doc,keydir} + +# copy conf, src, doc +cp -a src doc conf ~/.gitolite +cp conf/example.conf ~/.gitolite/conf/gitolite.conf From 00b4baa435eb61f55f7d460b1a34a237a795ba97 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 27 Aug 2009 15:24:23 +0530 Subject: [PATCH 032/850] doc changes after split --- doc/0-INSTALL.mkd | 84 ++++++++++++++++++ doc/1-migrate.mkd | 3 + doc/2-admin.mkd | 35 ++++++++ doc/3-faq-tips-etc.mkd | 38 +++++++++ doc/INSTALL.mkd | 189 ----------------------------------------- doc/TODO | 8 -- 6 files changed, 160 insertions(+), 197 deletions(-) create mode 100644 doc/0-INSTALL.mkd create mode 100644 doc/1-migrate.mkd create mode 100644 doc/2-admin.mkd create mode 100644 doc/3-faq-tips-etc.mkd delete mode 100644 doc/INSTALL.mkd diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd new file mode 100644 index 0000000..9a8f2fc --- /dev/null +++ b/doc/0-INSTALL.mkd @@ -0,0 +1,84 @@ +# installing gitolite + +### pre-requisites + +One of the big needs I'm trying to fill here is people who do not have root +access, permissions to create other userids, etc. This could be a typical +hosting provider type of thing, or -- in a corporate setting -- a very tightly +controlled server. + +Gitolite requires these: + + * git itself, the more recent the better + * perl, typically installed with git, since git sort of needs it; any + version that includes `Data::Dumper`[1] will do. + * one user account on the server, with password access [2] + +### quickinstall + +I assume all the files pertaining to this software are untarred and available +in the current directory. + +A quick install, taking all the defaults, can be done with the `install.sh` +script in the `src` directory. + +Note: + + * At present the location of `~/.gitolite.rc` is fixed (maybe later I'll + change it to a "git config" variable). + + If you edit it and change any paths, be sure to keep the perl syntax -- + you *don't* have to know perl to do so, it's fairly easy to guess in this + limited case. And of course, make sure you adjust the commands shown + above to suit the new locations + + * the config file is (by default) at `~/.gitolite/conf/gitolite.conf`. + Edit the file as you wish. The comments in the file ought to be clear + enough but let me know if not + + * if you want to bring in existing (bare, server) repos into gitolite, this + should work: + * backup the repo, then move it to `$BASE_REPO` + * copy `$GL_ADMINDIR/src/update-hook.pl` to + `[reponame].git/hooks/update` -- if you don't do this, per branch + restrictions will not work + * then update the keys and the config file and "compile" + +### Footnotes: + +[1] Actually, due to the way gitolite is architected, you can manage +without `Data::Dumper` on the server if you have no choice. Only +`gl-compile-conf` needs it, so just run that on some other machine and copy +the two output files across. Cumbersome but doable... the advantage of +separating all the hard work into a manually-run piece :) + +[2] If you have *only* pubkey access, and **no** password access, then your +pubkey is already in the server's `~/.ssh/authorized_keys`. If you also need +to access git as a developer (clone, push, etc), do *not* submit this same +pubkey to gitolite -- it won't work. + +Instead, create a different keypair for your "developer" role (by, e.g., +`ssh-keygen -t rsa -f ~/.ssh/gitdev`), then give `~/.ssh/gitdev.pub` to +gitolite as "yourname.pub", just like you would do for any other user. + +Then you create a suitable `~/.ssh/config` to use the correct key +automatically, something like this: + + host gitadm + hostname my.server + user my_userid_on_server + + host gitdev + hostname my.server + user my_userid_on_server + identityfile ~/.ssh/gitdev + +From now on, `ssh gitadm` will get you a command line on the server, to do +gitolite admin and other work. And your repository URLs would look like +`gitdev:reponame.git`. Very, very, simple... + +And as with gitosis, there's more "ssh" magic than "git" magic here :-) + +---- + +gitolite is released under the GPL v2 license. See COPYING for details diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd new file mode 100644 index 0000000..c1061cb --- /dev/null +++ b/doc/1-migrate.mkd @@ -0,0 +1,3 @@ +# migrating from gitosis to gitolite + + diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd new file mode 100644 index 0000000..8c9ae56 --- /dev/null +++ b/doc/2-admin.mkd @@ -0,0 +1,35 @@ +# administering and running gitolite + +### administer + + * ask each user who will get access to send you a public key. See other + sources (for example + [here](http: /sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key)) + for how to do this + * for each "user" in `$GL_CONF`, copy their public key to a file called + "user.pub" in `$GL_KEYDIR`. For example, mine would be called + "sitaram.pub" + * edit the config file (`$GL_CONF`) to add the new users in whatever way you + like + * backup your `~/.ssh/authorized_keys` file if you feel nervous :-) + * cd to `$GL_ADMINDIR` and run `src/gl-compile-conf` + +That should be it, really. However, if you want to be doubly sure, or maybe +the first couple of times you use it, you may want to check these: + + * check the outputs + + * `~/.ssh/authorized_keys` should contain one line for each "user" pub + key added, between two "marker" lines (which you should please please + not remove!). The line should contain a "command=" pointing to a + `$GL_ADMINDIR/src/gl-auth-command` file, then some sshd restrictions, the + key, etc. + * `$GL_CONF_COMPILED` (default + `~/.gitolite/conf/gitolite.conf-compiled.pm`) should contain an + expanded list of the access control rules. It may look a little long, + but it's fairly intuitive! + + * if the run threw up any "initialising empty repo" messages, check the + individual repos (inside `$REPO_BASE`) if you wish. Especially make sure + the `$REPO_BASE/[reponame].git/hooks/update` got copied OK and is + executable diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd new file mode 100644 index 0000000..ed3754e --- /dev/null +++ b/doc/3-faq-tips-etc.mkd @@ -0,0 +1,38 @@ +# assorted faqs, tips, and notes on gitolite + +### errors, warnings, notes... + + * cloning an empty repo is only possible with clients greater than 1.6.2. + So at least one of your clients needs to have a recent git. Once at least + one commit has been made, older clients can also use it. + + * when you clone an empty repo, git seems to complain about the remote + hanging up or something. I have no idea what that is, but it doesn't seem + to hurt anything. This happens even in normal git, not just gitolite. + + * once in a while, if you're feeling particularly BOFH-ish, take a look at + `$GL_ADMINDIR/log` :-) + +### special cases + +#### one user, many keys + +Sometimes the same user needs to access the server from differnt machines +(like a desktop and a laptop, for instance). Gitolite needs to be given all +these public keys, but associate *all* of them with the same user. + +Recall from the "admin" document that each "user" has one public key file +called "user.pub", which seems to imply a one-to-one match. + +But this is not strictly true -- gitolite allows a *filename* to have a small +"location" piece attached to it. So you can have "sitaram@laptop.pub" and +"sitaram@desktop.pub", for instance, and they'll all be treated as keys for +"sitaram". Just add both the files to "keydir/", and use the username +"sitaram" (*without* the "@location" part) in your `gitolite.conf` file. + +Advantages: if a user reports *one of his keys* is lost or needs replacing, +it's easy to remove or replace just that. + +(Contrast with gitosis, which keeps multiple entries in the same "user.pub" +file. Deleting or changing one of the keys involves editing the file and +figuring out which key is the right one!) diff --git a/doc/INSTALL.mkd b/doc/INSTALL.mkd deleted file mode 100644 index 0df9894..0000000 --- a/doc/INSTALL.mkd +++ /dev/null @@ -1,189 +0,0 @@ -In this document: - - * pre-requisites - * quickinstall - * install notes - * administer - * run - * special cases - * errors, warnings, etc - ----- - -### pre-requisites - -One of the big needs I'm trying to fill here is people who do not have root -access, permissions to create other userids, etc. This could be a typical -hosting provider type of thing, or -- in a corporate setting -- a very tightly -controlled server. - -Gitolite requires these: - - * git itself, the more recent the better - * perl, typically installed with git, since git sort of needs it; any - version that includes `Data::Dumper`[1] will do. - * one user account on the server, with password access [2] - -### quickinstall - -I assume all the files pertaining to this software are untarred and available -in the current directory. - -A quick install, taking all the defaults, can be done with the following -commands; just copy and paste them into your shell: - - # this one is fixed to the location shown - cp example.gitolite.rc ~/.gitolite.rc - - # the destinations below are defaults; if you change the paths in the "rc" - # file above, these destinations also must change accordingly - - # mkdir $REPO_BASE, $GL_ADMINDIR, and $GL_KEYDIR - mkdir ~/repositories - mkdir ~/.gitolite - mkdir ~/.gitolite/keydir - - # copy sample conf to $GL_CONF - cp example.conf ~/.gitolite/gitolite.conf - - # copy the 3 programs to $GL_ADMINDIR - cp update-hook.pl ~/.gitolite - cp gl-auth-command ~/.gitolite - cp gl-compile-conf ~/.gitolite - - # optional; copy the documents also (if you untarred the package into a - # temporary directory and need to get rid of it) - cp INSTALL README.markdown ~/.gitolite - -### install notes - - * At present the location of `~/.gitolite.rc` is fixed (maybe later I'll - change it to a "git config" variable). - - If you edit it and change any paths, be sure to keep the perl syntax -- - you *don't* have to know perl to do so, it's fairly easy to guess in this - limited case. And of course, make sure you adjust the commands shown - above to suit the new locations - - * the config file is (by default) at `~/.gitolite/gitolite.conf`. - Edit the file as you wish. The comments in the file ought to be clear - enough but let me know if not - - * if you want to bring in existing (bare, server) repos into gitolite, - this should work: - * backup the repo, then move it to `$BASE_REPO` - * copy `$GL_ADMINDIR/update-hook.pl` to `[reponame].git/hooks/update` -- - if you don't do this, per branch restrictions will not work - * then update the keys and the config file and "compile" - -### administer - - * ask each user who will get access to send you a public key. See other - sources (for example - [here](http://sitaramc.github.com/0-installing/2-access-gitosis.html#generating_a_public_key)) - for how to do this - * for each "user" in `$GL_CONF`, copy their public key to a file called - "user.pub" in `$GL_KEYDIR`. For example, mine would be called - "sitaram.pub" - * edit the config file (`$GL_CONF`) to add the new users in whatever way you - like - * backup your `~/.ssh/authorized_keys` file if you feel nervous :-) - * cd to `$GL_ADMINDIR` and run `./gl-compile-conf` - -#### optional -- if you want to be doubly sure - -It should all work, but the first couple of times you may want to check these - - * check the outputs - - * `~/.ssh/authorized_keys` should contain one line for each "user" pub - key added, between two "marker" lines (which you should please please - not remove!). The line should contain a "command=" pointing to a - `$GL_ADMINDIR/gl-auth-command` file, then some sshd restrictions, the - key, etc. - * `$GL_CONF_COMPILED` (default - `~/.gitolite/gitolite.conf-compiled.pm`) should contain an - expanded list of the access control rules. It may look a little long, - but it's fairly intuitive! - - * if the run threw up any "initialising empty repo" messages, check the - individual repos (inside `$REPO_BASE`) if you wish. Especially make sure - the `$REPO_BASE/[reponame].git/hooks/update` got copied OK and is - executable - -### run - -Just use it as normal. Every new repo mentioned has been created already, so -(as long as your clients are using git > 1.6.2), you can just clone it. - -And once in a while, if you're feeling particularly BOFH-ish, take a look at -`$GL_ADMINDIR/log` :-) - -### special cases - -#### one user, many keys - -Sometimes the same user needs to access the server from differnt machines -(like a desktop and a laptop, for instance). Gitolite needs to be given all -these public keys, but associate *all* of them with the same user. - -Recall from the "administer" section above that each "user" has one public key -file called "user.pub", which seems to imply a one-to-one match. - -But this is not strictly true -- gitolite allows a *filename* to have a small -"location" piece attached to it. So you can have "sitaram@laptop.pub" and -"sitaram@desktop.pub", for instance, and they'll all be treated as keys for -"sitaram". Just add both the files to "keydir/", and use the username -"sitaram" (*without* the "@location" part) in your `gitolite.conf` file. - -Advantages: if a user reports *one of his keys* is lost or needs replacing, -it's easy to remove or replace just that. - -(Gitosis keeps multiple entries in the same "user.pub", which means to delete -or change one of the keys you have to edit the file and figure out which of -the 2 or more long lines should be removed). - -### errors, warnings, etc - - * when you clone an empty repo, git seems to complain about the remote - hanging up or something. I have no idea what that is, but it doesn't seem - to hurt anything. This happens even in normal git, not just gitolite. - -### Footnotes: - -[1] Actually, due to the way gitolite is architected, you can manage -without `Data::Dumper` on the server if you have no choice. Only -`gl-compile-conf` needs it, so just run that on some other machine and copy -the two output files across. Cumbersome but doable... the advantage of -separating all the hard work into a manually-run piece :) - -[2] If you have *only* pubkey access, and **no** password access, then your -pubkey is already in the server's `~/.ssh/authorized_keys`. If you also need -to access git as a developer (clone, push, etc), do *not* submit this same -pubkey to gitolite -- it won't work. - -Instead, create a different keypair for your "developer" role (by, e.g., -`ssh-keygen -t rsa -f ~/.ssh/gitdev`), then give `~/.ssh/gitdev.pub` to -gitolite as "yourname.pub", just like you would do for any other user. - -Then you create a suitable `~/.ssh/config` to use the correct key -automatically, something like this: - - host gitadm - hostname my.server - user my_userid_on_server - - host gitdev - hostname my.server - user my_userid_on_server - identityfile ~/.ssh/gitdev - -From now on, `ssh gitadm` will get you a command line on the server, to do -gitolite admin and other work. And your repository URLs would look like -`gitdev:reponame.git`. Very, very, simple... - -And as with gitosis, there's more "ssh" magic than "git" magic here :-) - ----- - -gitolite is released under the GPL v2 license. See COPYING for details diff --git a/doc/TODO b/doc/TODO index 7f3a611..9c6faef 100644 --- a/doc/TODO +++ b/doc/TODO @@ -1,11 +1,3 @@ - * a proper INSTALL file with clear instructions for non-experts - * make a proper test suite - * `use Git` instead of run git commands: I don't run too many git commands - in this and Git.pm just runs the same commands (per the documentation), so - it's sorta moot right now, but it's worth trying - * change the "rc" file to use "gitconfig" instead... - - * check user/group names with some suitable regex From 3161b0ac86d1de03499ea2122ed3f957d0b96acc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 28 Aug 2009 06:26:23 +0530 Subject: [PATCH 033/850] migration document added --- doc/1-migrate.mkd | 88 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd index c1061cb..045946c 100644 --- a/doc/1-migrate.mkd +++ b/doc/1-migrate.mkd @@ -1,3 +1,91 @@ # migrating from gitosis to gitolite +Migrating from gitosis to gitolite is pretty easy, because the basic design is +the same. The differences are: + * gitolite does not use a special repo for the configuration, pubkeys, etc. + You can choose to version that directory but it is not required that you + do so + +Here's how we migrated my work repos: + +1. login as the `git` user on the server, and get a bash shell prompt + +2. **disable gitosis** by renaming `/usr/bin/gitosis-serve` to something + else. This will prevent users from pushing anything while you do the + backup, migration, etc. + +3. For added safety, **delete** the post-update hook that gitosis-admin + installed + + rm ~/repositories/gitosis-admin.git/hooks/post-update + + or at least rename it to `.sample` like all the other hooks hanging + around, or edit it and comment out the line that calls `gitosis-run-hook + post-update`. + + If you do not do this, an accidental push to the gitosis-admin repo will + mess up your `~/.ssh/authorized_keys` file + +4. take a **backup** of the `~/repositories` directory + +5. untar gitolite to some temporary directory and follow the instructions to + **install** it. Some of the steps (like `mkdir ~/repositories`) will + fail, but this is expected. Once this is done, cd to the `$GL_ADMINDIR` + (by default `~/.gitolite`) + + cd ~/.gitolite + +6. **convert** your gitosis config file: + + src/conf-convert.pl < ~/.gitosis.conf > conf/gitolite.conf + + be sure to check the file to make sure it converted correctly + +7. **copy** the update hook to each of the existing repos + + for i in ~/repositories/*.git + do + cp src/update-hook.pl $i/hooks/update + done + +8. **copy** the keys from gitosis's keydir + + cp ~/repositories/gitosis-admin.git/gitosis-export/keydir/* keydir + +9. **Important: expand** any multi-key filess you may have. See the "faq, + tips, etc" document in the doc directory for an explanation of what + multi-keys are, how gitosis does them and how gitolite does it + differently. + + You can split the keys manually, or use the following code (just + copy-paste it into you xterm): + + wc -l keydir/*.pub | grep -v total | grep -v -w 1 | while read a b + do + i=1 + cat $b|while read l + do + echo "$l" > ${b%.pub}@$i.pub + (( i++ )) + done + v $b $b.done + done + + This will split each multi-key file (say "sitaram.pub") into individual + files called "sitaram@1.pub", "sitaram@2.pub", etc., and rename the + original to "sitaram.pub.done" so gitolite won't pick it up. + + At this point you can rename the split parts more appropriately, like + "sitaram@laptop.pub" and "sitaram@desktop.pub" or whatever. *Please check + the files to make sure this worked properly* + +10. **edit** `~/.ssh/authorized_keys` and **carefully** remove all the lines + containing "gitosis-serve", as well as the marker line that says + "auto-generated by gitosis, DO NOT REMOVE", then save the file. If the + file did not have any other keys and is now empty, don't worry -- save it + anyway because gitolite expects the file to be present (even if it is + empty). + +At this point you're ready to "compile" the configuration. See the "admin" +document for what to do, and how to check the outputs, etc. From 491b3fac36137d33456dfcaa6684f1ef5376009f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 28 Aug 2009 07:16:40 +0530 Subject: [PATCH 034/850] you can't array-deref an undefined value! --- src/update-hook.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/update-hook.pl b/src/update-hook.pl index 709266e..470cf8f 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -68,8 +68,8 @@ $perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); $perm = '+' if $ref =~ m(refs/heads/) and $oldsha ne $merge_base; my @allowed_refs; -push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}} }; -push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{'@all'} }; +push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}} || [] }; +push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{'@all'} || [] }; for my $refex (@allowed_refs) # refex? sure -- a regex to match a ref against :) { From 4bea8a9ae7c58d0e5cd27082ae3b25136379c68d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 28 Aug 2009 09:41:52 +0530 Subject: [PATCH 035/850] cp -a changed to cp -R for solaris compat --- src/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/install.sh b/src/install.sh index c2156da..5b9e537 100755 --- a/src/install.sh +++ b/src/install.sh @@ -21,5 +21,5 @@ mkdir ~/.gitolite mkdir ~/.gitolite/{src,conf,doc,keydir} # copy conf, src, doc -cp -a src doc conf ~/.gitolite +cp -R src doc conf ~/.gitolite cp conf/example.conf ~/.gitolite/conf/gitolite.conf From 23e4c209eb671e50a7b391f7593de5a999e1a100 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 28 Aug 2009 18:29:05 +0530 Subject: [PATCH 036/850] README: added para about selective rewind, plus some minor fixes --- README.mkd | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/README.mkd b/README.mkd index e7d557e..aa97140 100644 --- a/README.mkd +++ b/README.mkd @@ -23,7 +23,7 @@ a typical $DAYJOB setting, there are some issues: and be done * often, "python-setuptools" isn't installed (and on a Solaris9 I was trying to help remotely, we never did manage to install it eventually) - * the most requested feature (see "what's extra?") had to be written anyway + * the most requested feature (see "what's new?") had to be written anyway ### what's gone @@ -45,15 +45,17 @@ Per-branch permissions. You will not believe how often I am asked this at $DAYJOB. This is almost the single reason I started *thinking* about rolling my own gitosis in the first place. +It's not just "read-only" versus "read-write". Rewinding a branch (aka "non +fast forward push") is potentially dangerous, but sometimes needed. So is +deleting a branch (which is really just an extreme form of rewind). I needed +something in between allowing anyone to do it (the default) and disabling it +completely (`receive.denyNonFastForwards` or `receive.denyDeletes`). + Take a look at the example config file in the repo to see how I do this. I copied the basic idea from `update-hook-example.txt` (it's one of the "howto"s -that come with the git source tree). This includes not just who can push to -what branch, but also whether they are allowed to rewind it or not (non-ff -push). - -However, please note the difference in the size and complexity of the -*operational code* between the update hook in that example, and in mine :-) -The reason is in the next section. +that come with the git source tree). However, please note the difference in +the size and complexity of the *operational code* between the update hook in +that example, and in mine :-) The reason is in the next section. ### the workflow @@ -71,8 +73,7 @@ So I changed the workflow completely: * all admin changes happen *on the server*, in a special directory that contains the config and the users' pubkeys. But there's no commit and - push afterward. Nothing prevents you from version-controlling that - directory if you wish to, but it's not *required* + push afterward * instead, after making changes, you "compile" the configuration. This refreshes `~/.ssh/authorized_keys`, as well as puts a parsed form of the access list in a file for the other two pieces to use. @@ -80,15 +81,20 @@ So I changed the workflow completely: The pre-parsed form is basically a huge perl variable. It's human readable too (never mind what the python guys say!) -Advantages: all the complexity of parsing and error checking the parse is done -away from the two places where the actual access control happens, which are: +So the admin knows immediately if the config file had any problems, which is +good. Also, the relatively complex parse code is not part of the actual +access control points, which are: * the program that is run via `~/.ssh/authorized_keys` (I call it `gl-auth-command`, equivalent to `gitosis-serve`); this decides whether git should even be allowed to run (basic R/W/no access) * the update-hook on each repo, which decides the per-branch permissions ----- +### footnotes [1] I hate whitespace to mean anything significant except for text; this is a personal opinion *only*, so pythonistas please back off :-) + +### contact + +sitaramc@gmail.com From 352208759189bfa5edc8743f5cbb9e5a0d60ba75 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 29 Aug 2009 11:36:26 +0530 Subject: [PATCH 037/850] compile: REPO_BASE need not be under $HOME In the "create new repos" loop, we need an absolute value for REPO_BASE, in order to be able to chdir back and forth. But (taking the "normal user with no privileges" assumption too far!) we assumed REPO_BASE would be within $HOME, and relative to it. So it fails when someone wants the repo_base elsewhere. Now we don't prefix $HOME if REPO_BASE is already absolute (begins with a "/") bug reported by evocallaghan --- conf/example.gitolite.rc | 2 +- src/gl-compile-conf | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 7a5068d..44413a0 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -1,6 +1,6 @@ # this is meant to be pulled into a perl program using "do" -# base directory for all the repos +# base directory for all the repos (absolute, or relative to $HOME) $REPO_BASE="repositories"; # gitolite admin directory, files, etc diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 3388979..811c325 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -184,7 +184,10 @@ close $compiled_fh or die "close compiled-conf failed: $!"; # did not have that luxury, so it was forced to detect the first push and # create it then -my_chdir("$ENV{HOME}/$REPO_BASE"); +# repo-base needs to be an absolute path for this loop to work right +# so if it was not already absolute, prefix $HOME. +my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +my_chdir("$repo_base_abs"); for my $repo (keys %repos) { unless (-d "$repo.git") @@ -194,7 +197,7 @@ for my $repo (keys %repos) system("git init --bare"); system("cp $GL_ADMINDIR/src/update-hook.pl hooks/update"); system("chmod 755 hooks/update"); - my_chdir("$ENV{HOME}/$REPO_BASE"); + my_chdir("$repo_base_abs"); } } From d27af37d21bcfd9ccc212743d3d3f1a004a25787 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 28 Aug 2009 19:09:31 +0530 Subject: [PATCH 038/850] faq-tips-etc: completely revamped; big "differences from gitosis" section, etc --- doc/3-faq-tips-etc.mkd | 113 +++++++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 16 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index ed3754e..bd66101 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -1,5 +1,14 @@ # assorted faqs, tips, and notes on gitolite +In this document: + + * errors, warnings, notes... + * differences from gitosis + * one user, many keys + * who am I? + * cool ideas I want feedback on + * developer specific branches + ### errors, warnings, notes... * cloning an empty repo is only possible with clients greater than 1.6.2. @@ -13,26 +22,98 @@ * once in a while, if you're feeling particularly BOFH-ish, take a look at `$GL_ADMINDIR/log` :-) -### special cases +### differences from gitosis + +Apart from the big ones listed in the top level README, and subjective ones +like "better config file format", there are some small, but significant and +concrete, differences from gitosis. + +#### built-in logging + +...just in case of emergency :-) + +Let's say you gave a dev the right to rewind a branch and he went and rewound +it all the way, or pushed something drastically different on it. Now you need +to recover the commit that got wiped out. + +If you'd remembered to `git config core.logAllRefUpdates` for that repo, or +globally, you'd be fine -- the reflog will tell you. Otherwise you'd be left +grubbing around in `git fsck --unreachable` a bit :-( + +And even if you recover the correct commit, you'll never know *who* did it -- +not unless you add a one-line patch to gitosis, plus a `post-receive` hook to +every repository. + +With gitolite, there's a log file in `$GL_ADMINDIR` that contains lines like +this [I have abbreviated the SHAs for brevity in this document; the actual log +file will have all 40 characters]: + + +: username reponame refs/heads/branchname d0188d1 c5c00b6 + +The "+" at the start indicates a non-fast forward update, in this case from +d0188d1 to c5c00b6 So d0188d1 is the one to restore! Can it get easier? #### one user, many keys -Sometimes the same user needs to access the server from differnt machines -(like a desktop and a laptop, for instance). Gitolite needs to be given all -these public keys, but associate *all* of them with the same user. +I have a laptop and a desktop I need to access the server from. I have +different private keys on them, but as far as gitolite is concerned both of +them should be treated as "sitaram". How does this work? -Recall from the "admin" document that each "user" has one public key file -called "user.pub", which seems to imply a one-to-one match. +In gitosis, the admin creates a single "sitaram.pub" containing one line for +each of my pubkeys. In gitolite, we keep them separate: "sitaram@laptop.pub" +and "sitaram@desktop.pub". The part before the "@" is the username, so +gitolite knows these two keys belong to the same person. -But this is not strictly true -- gitolite allows a *filename* to have a small -"location" piece attached to it. So you can have "sitaram@laptop.pub" and -"sitaram@desktop.pub", for instance, and they'll all be treated as keys for -"sitaram". Just add both the files to "keydir/", and use the username -"sitaram" (*without* the "@location" part) in your `gitolite.conf` file. +I think this is easier to maintain if you have to delete or change one of +those keys. -Advantages: if a user reports *one of his keys* is lost or needs replacing, -it's easy to remove or replace just that. +#### who am I? -(Contrast with gitosis, which keeps multiple entries in the same "user.pub" -file. Deleting or changing one of the keys involves editing the file and -figuring out which key is the right one!) +As a developer, I send a file called "id_rsa.pub" to the gitolite admin. He +would rename it to "sitaram.pub" and put it in the key directory. Then he'd +add "sitaram" to the config file for the repos which I have access to. + +But he could have called me "foobar" instead of "sitaram" -- as long as he +uses it consistently, it'll all work the same and look the same to me, because +the public key is all that matters. + +So do I have no reason to know what the admin named me? Well -- maybe (see +next section for one possible use). Anyway how do I find out? + +In gitolite, it's simple: just ask nicely :-) + + $ ssh git@my.gitolite.server + PTY allocation request failed on channel 0 + no SSH_ORIGINAL_COMMAND? I'm not a shell, sitaram! + +### cool ideas + +#### developer specific branches + +So I know what gitolite calls me. Big deal... who cares? + +Here is a cool idea: allow me to create, rewind, or delete any branch whose +full name matches this pattern: + + $PERSONAL_BRANCH_PREFIX/sitaram-.* + +The admin could set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate +this to all users. It could be something like `refs/heads/personal`, which +means all such branches will show up in `git branch` lookups and `git clone` +will fetch them. Or he could use, say, `refs/personal`, which means it won't +show up in any normal "branch-y" commands and stuff, and generally be much +less noisy. + +Yes, I know git is all about allowing private branches, but in a corporate +environment it's not always possible to pull from a co-worker, for the same +reasons you don't have anonymous access (like the git:// protocol). A normal +developer workstation cannot do authentication, so how would they know who's +pulling? This is a perfect way to share code *without* cluttering the global +namespace, and each developer controls his/her own set of branches! + +The amount of code needed? *One line!* I'll spend about 3x more on declaring +and initialising the new variable, and 30x more on documenting it :-) + +**That** is how neatly gitolite is designed... + +/me pats himself on the back ;-) From 4c2c55f2d17ef4dcc0934e7bd3c1588de4c9c498 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 29 Aug 2009 10:51:48 +0530 Subject: [PATCH 039/850] admin doc: clarified the instructions a little more ...it seems some admins are, well, not quite ready to be admins :) (also some minor typo fixes slipped in) --- doc/1-migrate.mkd | 6 +++--- doc/2-admin.mkd | 20 +++++++++++++------- doc/3-faq-tips-etc.mkd | 2 +- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd index 045946c..ca520c6 100644 --- a/doc/1-migrate.mkd +++ b/doc/1-migrate.mkd @@ -53,13 +53,13 @@ Here's how we migrated my work repos: cp ~/repositories/gitosis-admin.git/gitosis-export/keydir/* keydir -9. **Important: expand** any multi-key filess you may have. See the "faq, +9. **Important: expand** any multi-key files you may have. See the "faq, tips, etc" document in the doc directory for an explanation of what multi-keys are, how gitosis does them and how gitolite does it differently. You can split the keys manually, or use the following code (just - copy-paste it into you xterm): + copy-paste it into your xterm): wc -l keydir/*.pub | grep -v total | grep -v -w 1 | while read a b do @@ -69,7 +69,7 @@ Here's how we migrated my work repos: echo "$l" > ${b%.pub}@$i.pub (( i++ )) done - v $b $b.done + mv $b $b.done done This will split each multi-key file (say "sitaram.pub") into individual diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 8c9ae56..7f1c507 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -1,16 +1,22 @@ # administering and running gitolite +Note: some of the paths in this document use variable names. Just refer to +`~/.gitolite.rc` for the correct values, assuming you followed the +instructions in the "INSTALL" document. + ### administer * ask each user who will get access to send you a public key. See other sources (for example - [here](http: /sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key)) + [here](http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key)) for how to do this - * for each "user" in `$GL_CONF`, copy their public key to a file called - "user.pub" in `$GL_KEYDIR`. For example, mine would be called - "sitaram.pub" - * edit the config file (`$GL_CONF`) to add the new users in whatever way you - like + * rename each public key according to the user's name, with a `.pub` + extension, like `sitaram.pub` or `john-smith.pub`. You can also use + periods and underscores + * copy all these `*.pub` files to `$GL_KEYDIR` + * edit the config file (`$GL_CONF`) and give the new users permissions as + required. The users names should be exactly the same as their keyfile + names, but without the `.pub` extension * backup your `~/.ssh/authorized_keys` file if you feel nervous :-) * cd to `$GL_ADMINDIR` and run `src/gl-compile-conf` @@ -18,7 +24,7 @@ That should be it, really. However, if you want to be doubly sure, or maybe the first couple of times you use it, you may want to check these: * check the outputs - + * `~/.ssh/authorized_keys` should contain one line for each "user" pub key added, between two "marker" lines (which you should please please not remove!). The line should contain a "command=" pointing to a diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index bd66101..d7b3994 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -69,7 +69,7 @@ those keys. #### who am I? -As a developer, I send a file called "id_rsa.pub" to the gitolite admin. He +As a developer, I send a file called `id_rsa.pub` to the gitolite admin. He would rename it to "sitaram.pub" and put it in the key directory. Then he'd add "sitaram" to the config file for the repos which I have access to. From b1c329dbb64bc71e00b57e31bf255903479217be Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 30 Aug 2009 12:08:54 +0530 Subject: [PATCH 040/850] doc fixes: - install is even clearer now (I hope!), esp to people with root access who seem to expect something else :) - used path vars (from ~/.gitolite.rc) more consistently, and - added refeerences to ~/.gitolite.rc for resolving them --- doc/0-INSTALL.mkd | 43 +++++++++++++++++++++++++------------------ doc/1-migrate.mkd | 22 +++++++++++----------- doc/2-admin.mkd | 11 ++++------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 9a8f2fc..970237e 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -2,47 +2,54 @@ ### pre-requisites -One of the big needs I'm trying to fill here is people who do not have root -access, permissions to create other userids, etc. This could be a typical -hosting provider type of thing, or -- in a corporate setting -- a very tightly -controlled server. - -Gitolite requires these: +If you managed to install git, you might already have what gitolite needs: * git itself, the more recent the better * perl, typically installed with git, since git sort of needs it; any version that includes `Data::Dumper`[1] will do. * one user account on the server, with password access [2] -### quickinstall +A major objective is to allow use by people without root access, permissions +to create other userids, etc. If you have root, congratulations and all that, +but go add a user just for gitolite and do all this from that user. Really. +I see no earthly reason for this to run as root. -I assume all the files pertaining to this software are untarred and available -in the current directory. +And don't bug me about wanting to install it in `/opt` or whatever if you +haven't the time to read the docs or change a path in a config file. -A quick install, taking all the defaults, can be done with the `install.sh` -script in the `src` directory. +### quick install -Note: + * cd to the directory where you unpacked the source + * run `src/install.pl` and follow the prompts + +**When you are told to edit some file, please read the comments in the file**. +And if you can make some time to read the documentation, please do. +Especially if you have problems. + +Notes: * At present the location of `~/.gitolite.rc` is fixed (maybe later I'll - change it to a "git config" variable). + change it to a "git config" variable but I don't see much need right now) If you edit it and change any paths, be sure to keep the perl syntax -- you *don't* have to know perl to do so, it's fairly easy to guess in this limited case. And of course, make sure you adjust the commands shown above to suit the new locations - * the config file is (by default) at `~/.gitolite/conf/gitolite.conf`. - Edit the file as you wish. The comments in the file ought to be clear - enough but let me know if not + * the config file is (by default) at `~/.gitolite/conf/gitolite.conf`, + though you can change its location in the "rc" file. Edit the file as you + wish. The comments in the file ought to be clear enough but let me know + if not * if you want to bring in existing (bare, server) repos into gitolite, this - should work: + should work (refer to `~/.gitolite.rc` for *your* values of the pathnames + below): * backup the repo, then move it to `$BASE_REPO` * copy `$GL_ADMINDIR/src/update-hook.pl` to `[reponame].git/hooks/update` -- if you don't do this, per branch restrictions will not work - * then update the keys and the config file and "compile" + * then update the keys and the config file and "compile" (see "admin" + document) ### Footnotes: diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd index ca520c6..6ef0c90 100644 --- a/doc/1-migrate.mkd +++ b/doc/1-migrate.mkd @@ -7,7 +7,8 @@ the same. The differences are: You can choose to version that directory but it is not required that you do so -Here's how we migrated my work repos: +Here's how we migrated my work repos (note: substitute real paths, from your +`~/.gitolite.rc`, for `$REPO_BASE` and `$GL_ADMINDIR` below): 1. login as the `git` user on the server, and get a bash shell prompt @@ -18,7 +19,7 @@ Here's how we migrated my work repos: 3. For added safety, **delete** the post-update hook that gitosis-admin installed - rm ~/repositories/gitosis-admin.git/hooks/post-update + rm $REPO_BASE/gitosis-admin.git/hooks/post-update or at least rename it to `.sample` like all the other hooks hanging around, or edit it and comment out the line that calls `gitosis-run-hook @@ -27,31 +28,29 @@ Here's how we migrated my work repos: If you do not do this, an accidental push to the gitosis-admin repo will mess up your `~/.ssh/authorized_keys` file -4. take a **backup** of the `~/repositories` directory +4. take a **backup** of the `$REPO_BASE` directory 5. untar gitolite to some temporary directory and follow the instructions to - **install** it. Some of the steps (like `mkdir ~/repositories`) will - fail, but this is expected. Once this is done, cd to the `$GL_ADMINDIR` - (by default `~/.gitolite`) - - cd ~/.gitolite + **install** it using `src/install.pl` 6. **convert** your gitosis config file: + cd $GL_ADMINDIR src/conf-convert.pl < ~/.gitosis.conf > conf/gitolite.conf be sure to check the file to make sure it converted correctly -7. **copy** the update hook to each of the existing repos +7. **copy** the update hook to each of the existing repos (if you have repos + in subdirectories, this won't work as is; adapt it): - for i in ~/repositories/*.git + for i in $REPO_BASE/*.git do cp src/update-hook.pl $i/hooks/update done 8. **copy** the keys from gitosis's keydir - cp ~/repositories/gitosis-admin.git/gitosis-export/keydir/* keydir + cp $REPO_BASE/gitosis-admin.git/gitosis-export/keydir/* keydir 9. **Important: expand** any multi-key files you may have. See the "faq, tips, etc" document in the doc directory for an explanation of what @@ -61,6 +60,7 @@ Here's how we migrated my work repos: You can split the keys manually, or use the following code (just copy-paste it into your xterm): + cd $GL_ADMINDIR wc -l keydir/*.pub | grep -v total | grep -v -w 1 | while read a b do i=1 diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 7f1c507..ea8d06f 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -1,8 +1,7 @@ # administering and running gitolite -Note: some of the paths in this document use variable names. Just refer to -`~/.gitolite.rc` for the correct values, assuming you followed the -instructions in the "INSTALL" document. +*Note*: some of the paths in this document use variable names. Just refer to +`~/.gitolite.rc` for the correct values for *your* installation. ### administer @@ -30,10 +29,8 @@ the first couple of times you use it, you may want to check these: not remove!). The line should contain a "command=" pointing to a `$GL_ADMINDIR/src/gl-auth-command` file, then some sshd restrictions, the key, etc. - * `$GL_CONF_COMPILED` (default - `~/.gitolite/conf/gitolite.conf-compiled.pm`) should contain an - expanded list of the access control rules. It may look a little long, - but it's fairly intuitive! + * `$GL_CONF_COMPILED` should contain an expanded list of the access + control rules. It may look a little long, but it's fairly intuitive! * if the run threw up any "initialising empty repo" messages, check the individual repos (inside `$REPO_BASE`) if you wish. Especially make sure From 08305aa482abe5bb9d1219961dde421771fd4ce6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 30 Aug 2009 12:11:55 +0530 Subject: [PATCH 041/850] install streamlining: - install.sh is now install.pl (had to happen sooner or later!) - now handles updates more gracefully, doesn't overwrite important stuff :) - makes the install sequence much easier to understand (just run it and follow the prompts!) - made ~/.gitolite.rc much clearer to edit --- conf/example.gitolite.rc | 28 +++++++++++++++---- src/install.pl | 58 ++++++++++++++++++++++++++++++++++++++++ src/install.sh | 25 ----------------- 3 files changed, 81 insertions(+), 30 deletions(-) create mode 100755 src/install.pl delete mode 100755 src/install.sh diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 44413a0..72acc79 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -1,20 +1,38 @@ -# this is meant to be pulled into a perl program using "do" +# default paths for gitolite + +# please read comments before editing + +# this file is meant to be pulled into a perl program using "do" or "require". + +# You do NOT need to know perl to edit the paths; it should be fairly +# self-explanatory + +# -------------------------------------- + +# this is where the repos go. If you provide a relative path (not starting +# with "/"), it's relative to your $HOME. You may want to put in something +# like "/bigdisk" or whatever if your $HOME is too small for the repos, for +# example -# base directory for all the repos (absolute, or relative to $HOME) $REPO_BASE="repositories"; +# -------------------------------------- + +# I see no reason anyone may want to change the gitolite admin directory, but +# feel free to do so + # gitolite admin directory, files, etc $GL_ADMINDIR=$ENV{HOME} . "/.gitolite"; # -------------------------------------- -# the ones below can be left as they are, unless for some reason you want them -# elsewhere +# I see even less reason to change these, since they're all relative to the +# gitolite admin directory above, but hey it's *your* system... $GL_CONF="$GL_ADMINDIR/conf/gitolite.conf"; $GL_KEYDIR="$GL_ADMINDIR/keydir"; $GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm"; # -------------------------------------- -# this should be the last line in this file, per perl rules +# per perl rules, this should be the last line in such a file: 1; diff --git a/src/install.pl b/src/install.pl new file mode 100755 index 0000000..73fb118 --- /dev/null +++ b/src/install.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +our $REPO_BASE; +our $GL_ADMINDIR; +our $GL_CONF; + +# wrapper around mkdir; it's not an error if the directory exists, but it is +# an error if it doesn't exist and we can't create it +sub wrap_mkdir +{ + my $dir = shift; + -d $dir or mkdir($dir) or die "mkdir $dir failed: $!\n"; +} + +# the only path that is *fixed* (can't be changed without changing all 3 +# programs) is ~/.gitolite.rc + +my $glrc = $ENV{HOME} . "/.gitolite.rc"; +unless (-f $glrc) { + # doesn't exist. Copy it across, tell user to edit it and come back + system("cp conf/example.gitolite.rc $glrc"); + print STDERR "created $glrc\n"; + print STDERR "please edit it, set the paths as you like, and rerun this script\n"; + exit; +} + +# ok now $glrc exists; read it to get the other paths +unless (my $ret = do $glrc) +{ + die "parse $glrc failed: $@" if $@; + die "couldn't do $glrc: $!" unless defined $ret; + die "couldn't run $glrc" unless $ret; +} + +# mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist +wrap_mkdir( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +wrap_mkdir($GL_ADMINDIR); +# mkdir $GL_ADMINDIR's subdirs +for my $dir qw(conf doc keydir src) { + wrap_mkdir("$GL_ADMINDIR/$dir"); +} + +# "src" and "doc" will be overwritten on each install, but not conf +system("cp -R src doc $GL_ADMINDIR"); + +unless (-f $GL_CONF) { + system("cp conf/example.conf $GL_CONF"); + print STDERR < Date: Sun, 30 Aug 2009 13:27:18 +0530 Subject: [PATCH 042/850] minor: remove needless "our"s --- src/gl-auth-command | 2 -- src/update-hook.pl | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 1fd294c..8344435 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -24,8 +24,6 @@ use warnings; # ---------------------------------------------------------------------------- our $GL_ADMINDIR; -our $GL_CONF; -our $GL_KEYDIR; our $GL_CONF_COMPILED; our $REPO_BASE; our %repos; diff --git a/src/update-hook.pl b/src/update-hook.pl index 470cf8f..b4c4580 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -26,10 +26,7 @@ use warnings; # ---------------------------------------------------------------------------- our $GL_ADMINDIR; -our $GL_CONF; -our $GL_KEYDIR; our $GL_CONF_COMPILED; -our $REPO_BASE; our %repos; my $glrc = $ENV{HOME} . "/.gitolite.rc"; From b916a07d281c463db5554f8238e2ad3858b1744c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 28 Aug 2009 20:41:21 +0530 Subject: [PATCH 043/850] update hook: using non-std branches revealed an unnecessary check for refs/heads/; removed --- doc/0-INSTALL.mkd | 8 ++------ src/update-hook.pl | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 970237e..10450a2 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -10,12 +10,8 @@ If you managed to install git, you might already have what gitolite needs: * one user account on the server, with password access [2] A major objective is to allow use by people without root access, permissions -to create other userids, etc. If you have root, congratulations and all that, -but go add a user just for gitolite and do all this from that user. Really. -I see no earthly reason for this to run as root. - -And don't bug me about wanting to install it in `/opt` or whatever if you -haven't the time to read the docs or change a path in a config file. +to create other userids, etc. Even if you have root, please add a user just +for gitolite and do all this from that user. ### quick install diff --git a/src/update-hook.pl b/src/update-hook.pl index b4c4580..834a32f 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -60,9 +60,9 @@ chomp($merge_base = `git merge-base $oldsha $newsha`) my $perm = 'W'; # rewriting a tag is considered a rewind, in terms of permissions $perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); -# non-ff push to branch. Notice that branch delete looks like a rewind, as it -# should -$perm = '+' if $ref =~ m(refs/heads/) and $oldsha ne $merge_base; +# non-ff push to ref +# notice that ref delete looks like a rewind, as it should +$perm = '+' if $oldsha ne $merge_base; my @allowed_refs; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}} || [] }; From 78a10a1ee180201c9c9fc75f3f1a0459fa5edd83 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 30 Aug 2009 21:14:15 +0530 Subject: [PATCH 044/850] compile: another solaris compat fix, to do with "~" system("...") run from perl on sol does not seem to like "~" (regardless of what $SHELL is set to), so use $ENV{HOME} instead thanks again to evocallaghan --- src/gl-compile-conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 811c325..9affc31 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -232,8 +232,8 @@ close $newkeys_fh or die "close newkeys failed: $!"; # system("vim -d ~/.ssh/authorized_keys ~/.ssh/new_authkeys"); # all done; overwrite the file (use cat to avoid perm changes) -system("cat ~/.ssh/new_authkeys > ~/.ssh/authorized_keys"); -system("rm ~/.ssh/new_authkeys"); +system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys"); +system("rm $ENV{HOME}/.ssh/new_authkeys"); # if the gl admin directory (~/.gitolite) is itself a git repo, do an # autocheckin. nothing fancy; this is a "just in case" type of thing. From 53f1a77f7fe69ba08aaa14b549879b942dc62aca Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 30 Aug 2009 21:19:36 +0530 Subject: [PATCH 045/850] admin doc: clarify why authkeys is needed and what it does I was very insistently told by a user that I should just create the file if it does not exist, but this is as far as I am willing to go --- doc/2-admin.mkd | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index ea8d06f..ae65b72 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -9,14 +9,28 @@ sources (for example [here](http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key)) for how to do this + * rename each public key according to the user's name, with a `.pub` extension, like `sitaram.pub` or `john-smith.pub`. You can also use periods and underscores + * copy all these `*.pub` files to `$GL_KEYDIR` + * edit the config file (`$GL_CONF`) and give the new users permissions as required. The users names should be exactly the same as their keyfile names, but without the `.pub` extension + * backup your `~/.ssh/authorized_keys` file if you feel nervous :-) + * that's "backup" as in "copy", not "move". The next step won't work if + the file doesn't exist. Even an empty one is fine but it must be + present + * if you don't have an `~/.ssh/authorized_keys` file at all, you may + have logged in with a password, which in turn might mean you are not + familiar with ssh and authkeys etc. If so, please read up at least + [this](http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh), + and preferably also the man pages for sshd and sshd\_config, to make + sure you understand the security implications of what you are doing + * cd to `$GL_ADMINDIR` and run `src/gl-compile-conf` That should be it, really. However, if you want to be doubly sure, or maybe From abb4580d85c2979f6c7f2da198664fae3de07675 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 31 Aug 2009 07:58:08 +0530 Subject: [PATCH 046/850] compile: wrap the open call as well, plus better messages from both wrappers --- src/gl-compile-conf | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 9affc31..e702a1d 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -74,9 +74,13 @@ umask(0077); # subroutines # ---------------------------------------------------------------------------- -sub my_chdir -{ - chdir($_[0]) or die "chdir $_[0] failed: $!"; +sub wrap_chdir { + chdir($_[0]) or die "chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; +} + +sub wrap_open { + open (my $fh, $_[0], $_[1]) or die "open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; + return $fh; } sub expand_userlist @@ -106,8 +110,7 @@ sub expand_userlist # "compile" GL conf # ---------------------------------------------------------------------------- -open my $conf_fh, "<", $GL_CONF - or die "open conf failed: $!"; +my $conf_fh = wrap_open( "<", $GL_CONF ); # the syntax is fairly simple, so we parse it inline @@ -171,8 +174,7 @@ while (<$conf_fh>) } } -open my $compiled_fh, ">", $GL_CONF_COMPILED - or die "open compiled-conf failed: $!"; +my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]); close $compiled_fh or die "close compiled-conf failed: $!"; @@ -187,17 +189,17 @@ close $compiled_fh or die "close compiled-conf failed: $!"; # repo-base needs to be an absolute path for this loop to work right # so if it was not already absolute, prefix $HOME. my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); -my_chdir("$repo_base_abs"); +wrap_chdir("$repo_base_abs"); for my $repo (keys %repos) { unless (-d "$repo.git") { mkdir("$repo.git") or die "mkdir $repo.git failed: $!"; - my_chdir("$repo.git"); + wrap_chdir("$repo.git"); system("git init --bare"); system("cp $GL_ADMINDIR/src/update-hook.pl hooks/update"); system("chmod 755 hooks/update"); - my_chdir("$repo_base_abs"); + wrap_chdir("$repo_base_abs"); } } @@ -205,10 +207,8 @@ for my $repo (keys %repos) # "compile" ssh authorized_keys # ---------------------------------------------------------------------------- -open my $authkeys_fh, "<", $ENV{HOME} . "/.ssh/authorized_keys" - or die "open authkeys failed: $!"; -open my $newkeys_fh, ">", $ENV{HOME} . "/.ssh/new_authkeys" - or die "open newkeys failed: $!"; +my $authkeys_fh = wrap_open( "<", $ENV{HOME} . "/.ssh/authorized_keys" ); +my $newkeys_fh = wrap_open( ">", $ENV{HOME} . "/.ssh/new_authkeys" ); # save existing authkeys minus the GL-added stuff while (<$authkeys_fh>) { @@ -218,7 +218,7 @@ while (<$authkeys_fh>) # add our "start" line, each key on its own line (prefixed by command and # options, in the standard ssh authorized_keys format), then the "end" line. print $newkeys_fh "# gitolite start\n"; -my_chdir($GL_KEYDIR); +wrap_chdir($GL_KEYDIR); for my $pubkey (glob("*.pub")) { my $user = $pubkey; $user =~ s/(\@.+)?\.pub$//; @@ -237,7 +237,7 @@ system("rm $ENV{HOME}/.ssh/new_authkeys"); # if the gl admin directory (~/.gitolite) is itself a git repo, do an # autocheckin. nothing fancy; this is a "just in case" type of thing. -my_chdir($GL_ADMINDIR); +wrap_chdir($GL_ADMINDIR); if (-d ".git") { system("git add -A conf keydir"); # stage all operational data From dd13de2d14bb11a51cc9424cd5eef8e7a3f4f6f3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 30 Aug 2009 13:42:53 +0530 Subject: [PATCH 047/850] update hook/conf: $PERSONAL branch prefix wicked cool to get it in one line of code! --- conf/example.gitolite.rc | 10 ++++++++++ src/update-hook.pl | 2 ++ 2 files changed, 12 insertions(+) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 72acc79..f63a411 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -33,6 +33,16 @@ $GL_CONF="$GL_ADMINDIR/conf/gitolite.conf"; $GL_KEYDIR="$GL_ADMINDIR/keydir"; $GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm"; +# -------------------------------------- +# personal branch prefix; leave it as is (empty) if you don't want to use the +# feature (see the "developer-specific branches" section in the "faq, tips, +# etc" document) +$PERSONAL=""; +# uncomment one of these if you do want it. I recommend this: +# $PERSONAL="refs/personal"; +# but if you want something more visible/noisy, use this: +# $PERSONAL="refs/heads/personal"; + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/src/update-hook.pl b/src/update-hook.pl index 834a32f..4a51d76 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -27,6 +27,7 @@ use warnings; our $GL_ADMINDIR; our $GL_CONF_COMPILED; +our $PERSONAL; our %repos; my $glrc = $ENV{HOME} . "/.gitolite.rc"; @@ -67,6 +68,7 @@ $perm = '+' if $oldsha ne $merge_base; my @allowed_refs; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}} || [] }; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{'@all'} || [] }; +push @allowed_refs, "$PERSONAL/$ENV{GL_USER}-" if $PERSONAL; for my $refex (@allowed_refs) # refex? sure -- a regex to match a ref against :) { From 62d89bf8f8f817fa148fa2b0188a1ea8dc0553ed Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 31 Aug 2009 20:50:47 +0530 Subject: [PATCH 048/850] gl-auth: R_COMMANDS adds "git-upload-archive" --- src/gl-auth-command | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 8344435..012398f 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -42,7 +42,7 @@ die "couldnt do perms file" unless (my $ret = do $GL_CONF_COMPILED); # definitions specific to this program # ---------------------------------------------------------------------------- -my $R_COMMANDS=qr/^git[ -]upload-pack$/; +my $R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/; my $W_COMMANDS=qr/^git[ -]receive-pack$/; my $REPONAME_PATT=qr(^[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern From 5d4d5184b4c095ddce41e9c7fe779e6d44649aff Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Sep 2009 19:36:00 +0530 Subject: [PATCH 049/850] sources: 1-line all the "do"s for brevity and clarity and yes, brevity and clarity "do" go together in perl :) --- src/gl-auth-command | 10 ++-------- src/gl-compile-conf | 7 +------ src/install.pl | 7 +------ src/update-hook.pl | 10 ++-------- 4 files changed, 6 insertions(+), 28 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 012398f..2ea2dbb 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -29,14 +29,8 @@ our $REPO_BASE; our %repos; my $glrc = $ENV{HOME} . "/.gitolite.rc"; -unless (my $ret = do $glrc) -{ - die "parse $glrc failed: $@" if $@; - die "couldn't do $glrc: $!" unless defined $ret; - die "couldn't run $glrc" unless $ret; -} - -die "couldnt do perms file" unless (my $ret = do $GL_CONF_COMPILED); +die "parse $glrc failed: " . ($! or $@) unless do $glrc; +die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; # ---------------------------------------------------------------------------- # definitions specific to this program diff --git a/src/gl-compile-conf b/src/gl-compile-conf index e702a1d..8454eb4 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -48,12 +48,7 @@ our $GL_CONF_COMPILED; our $REPO_BASE; my $glrc = $ENV{HOME} . "/.gitolite.rc"; -unless (my $ret = do $glrc) -{ - die "parse $glrc failed: $@" if $@; - die "couldn't do $glrc: $!" unless defined $ret; - die "couldn't run $glrc" unless $ret; -} +die "parse $glrc failed: " . ($! or $@) unless do $glrc; # ---------------------------------------------------------------------------- # definitions specific to this program diff --git a/src/install.pl b/src/install.pl index 73fb118..6dec899 100755 --- a/src/install.pl +++ b/src/install.pl @@ -28,12 +28,7 @@ unless (-f $glrc) { } # ok now $glrc exists; read it to get the other paths -unless (my $ret = do $glrc) -{ - die "parse $glrc failed: $@" if $@; - die "couldn't do $glrc: $!" unless defined $ret; - die "couldn't run $glrc" unless $ret; -} +die "parse $glrc failed: " . ($! or $@) unless do $glrc; # mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist wrap_mkdir( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); diff --git a/src/update-hook.pl b/src/update-hook.pl index 4a51d76..526cf1d 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -31,14 +31,8 @@ our $PERSONAL; our %repos; my $glrc = $ENV{HOME} . "/.gitolite.rc"; -unless (my $ret = do $glrc) -{ - die "parse $glrc failed: $@" if $@; - die "couldn't do $glrc: $!" unless defined $ret; - die "couldn't run $glrc" unless $ret; -} - -die "couldnt do perms file" unless (my $ret = do $GL_CONF_COMPILED); +die "parse $glrc failed: " . ($! or $@) unless do $glrc; +die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; # ---------------------------------------------------------------------------- # start... From 208c401858b638ad07f736b43d7e96ae5bd0985e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Sep 2009 19:40:42 +0530 Subject: [PATCH 050/850] compile: chmod internal, and save "old" authkeys --- src/gl-compile-conf | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 8454eb4..0907655 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -193,7 +193,7 @@ for my $repo (keys %repos) wrap_chdir("$repo.git"); system("git init --bare"); system("cp $GL_ADMINDIR/src/update-hook.pl hooks/update"); - system("chmod 755 hooks/update"); + chmod 0755, "hooks/update"; wrap_chdir("$repo_base_abs"); } } @@ -223,10 +223,8 @@ for my $pubkey (glob("*.pub")) print $newkeys_fh "# gitolite end\n"; close $newkeys_fh or die "close newkeys failed: $!"; -# check what changes are being made; just a comfort factor -# system("vim -d ~/.ssh/authorized_keys ~/.ssh/new_authkeys"); - # all done; overwrite the file (use cat to avoid perm changes) +system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys"); system("rm $ENV{HOME}/.ssh/new_authkeys"); From 401c2f46d7f3cbbeeec807329fec502c9db6818d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Sep 2009 20:33:19 +0530 Subject: [PATCH 051/850] install: update hooks in all repos on upgrade --- src/install.pl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/install.pl b/src/install.pl index 6dec899..4624a74 100755 --- a/src/install.pl +++ b/src/install.pl @@ -51,3 +51,16 @@ unless (-f $GL_CONF) { (the "admin" document should help here...) EOF } + +# finally, any potential changes to src/update-hook.pl must be propagated to +# all the repos' hook directories +my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +# err, no need to get all worked up if you can't CD there -- this may be the +# very first run and it hasn't been created yet +if (chdir("$repo_base_abs")) { + for my $repo (`find . -type d -name "*.git"`) { + chomp ($repo); + system("cp $GL_ADMINDIR/src/update-hook.pl $repo/hooks/update"); + chmod 0755, "$repo/hooks/update"; + } +} From 4fa1ca665267f6d25c6339fe8823fab06ec70883 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Sep 2009 19:52:06 +0530 Subject: [PATCH 052/850] minor doc updates re directories etc --- conf/example.gitolite.rc | 3 ++- doc/3-faq-tips-etc.mkd | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index f63a411..0eb7ce6 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -19,7 +19,8 @@ $REPO_BASE="repositories"; # -------------------------------------- # I see no reason anyone may want to change the gitolite admin directory, but -# feel free to do so +# feel free to do so. However, please note that it *must* be an *absolute* +# path (i.e., starting with a "/" character) # gitolite admin directory, files, etc $GL_ADMINDIR=$ENV{HOME} . "/.gitolite"; diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index d7b3994..19ead5e 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -22,6 +22,12 @@ In this document: * once in a while, if you're feeling particularly BOFH-ish, take a look at `$GL_ADMINDIR/log` :-) + * if you specify a repo that is not at the top level `$REPO_BASE`, be sure + to manually create the intermediate directories first. For instance if + you specify a new repo called "a/b/c" to the config file and "compile", + the "compile" script will just `mkdir a/b/c.git`, assuming "a/b" has + already been created + ### differences from gitosis Apart from the big ones listed in the top level README, and subjective ones From 455ebe1bc99706efa7c1e6a6ba01793c0d8554c0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 2 Sep 2009 06:49:04 +0530 Subject: [PATCH 053/850] update hook: personal branches pattern, "-" becomes "/" --- doc/3-faq-tips-etc.mkd | 7 ++++--- src/update-hook.pl | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 19ead5e..ea3c3a7 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -98,10 +98,11 @@ In gitolite, it's simple: just ask nicely :-) So I know what gitolite calls me. Big deal... who cares? -Here is a cool idea: allow me to create, rewind, or delete any branch whose -full name matches this pattern: +Here is an idea: give every developer a personal "scratch" namespace within +which she can create, rewind, or delete any branch. For example, I would own +anything under - $PERSONAL_BRANCH_PREFIX/sitaram-.* + $PERSONAL_BRANCH_PREFIX/sitaram/ The admin could set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate this to all users. It could be something like `refs/heads/personal`, which diff --git a/src/update-hook.pl b/src/update-hook.pl index 526cf1d..41d6f76 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -62,7 +62,7 @@ $perm = '+' if $oldsha ne $merge_base; my @allowed_refs; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}} || [] }; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{'@all'} || [] }; -push @allowed_refs, "$PERSONAL/$ENV{GL_USER}-" if $PERSONAL; +push @allowed_refs, "$PERSONAL/$ENV{GL_USER}/" if $PERSONAL; for my $refex (@allowed_refs) # refex? sure -- a regex to match a ref against :) { From 804c70f570bafdbd1098e45b8de2f651edcae0cc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 6 Sep 2009 13:34:41 +0530 Subject: [PATCH 054/850] almost all src/conf: logging totally redone, upgrade doc added - logs go into $GL_ADMINDIR/logs by default, named by year-month - logfile name template (including dir prefix) now in $GL_LOGT - two new env vars passed down: GL_TS and GL_LOG (timestamp, logfilename) - log messages timestamps more compact, fields tab-delimited - old and new SHAs cut to 14 characters --- README.mkd | 5 ++++ conf/example.gitolite.rc | 28 +++++++++++++++++++++- doc/0-UPGRADE.mkd | 52 ++++++++++++++++++++++++++++++++++++++++ src/gl-auth-command | 31 ++++++++++++++++++------ src/gl-compile-conf | 6 +---- src/install.pl | 23 +++++++----------- src/update-hook.pl | 10 ++++---- 7 files changed, 123 insertions(+), 32 deletions(-) create mode 100644 doc/0-UPGRADE.mkd diff --git a/README.mkd b/README.mkd index aa97140..a49eb67 100644 --- a/README.mkd +++ b/README.mkd @@ -1,5 +1,10 @@ # gitolite +> [IMPORTANT: There is now an "upgrade" document in the "doc" directory; +> please read if upgrading gitolite] + +---- + Gitolite is the bare essentials of gitosis, with a completely different config file that allows (at last!) access control down to the branch level, including specifying who can and cannot *rewind* a given branch. It is diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 0eb7ce6..f5e64a1 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -1,4 +1,4 @@ -# default paths for gitolite +# paths and configuration variables for gitolite # please read comments before editing @@ -23,10 +23,27 @@ $REPO_BASE="repositories"; # path (i.e., starting with a "/" character) # gitolite admin directory, files, etc + $GL_ADMINDIR=$ENV{HOME} . "/.gitolite"; # -------------------------------------- +# templates for location of the log files and format of their names + +# I prefer this template (note the %y and %m placeholders) +# it produces files like `~/.gitolite/logs/gitolite-2009-09.log` + +$GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m.log"; + +# other choices are below, or you can make your own -- but PLEASE MAKE SURE +# the directory exists and is writable; gitolite won't do that for you (unless +# it is the default, which is "$GL_ADMINDIR/logs") + +# $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m-%d.log"; +# $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y.log"; + +# -------------------------------------- + # I see even less reason to change these, since they're all relative to the # gitolite admin directory above, but hey it's *your* system... @@ -35,15 +52,24 @@ $GL_KEYDIR="$GL_ADMINDIR/keydir"; $GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm"; # -------------------------------------- + # personal branch prefix; leave it as is (empty) if you don't want to use the # feature (see the "developer-specific branches" section in the "faq, tips, # etc" document) + $PERSONAL=""; + # uncomment one of these if you do want it. I recommend this: # $PERSONAL="refs/personal"; + # but if you want something more visible/noisy, use this: # $PERSONAL="refs/heads/personal"; +# NOTE: whatever value you choose, for security reasons it is better to make +# it fully qualified -- that is, starting with "refs/" + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; + +# vim: set syn=perl: diff --git a/doc/0-UPGRADE.mkd b/doc/0-UPGRADE.mkd new file mode 100644 index 0000000..4d55ac0 --- /dev/null +++ b/doc/0-UPGRADE.mkd @@ -0,0 +1,52 @@ +# upgrading gitolite atomically + +### general upgrade notes + +If you follow the steps below, you can make the upgrade "atomic", so you don't +have to do it at a "quiet" time or something. + +1. untar the new version to some temp directory and `cd` to it + +2. *prepare* the new version of `~/.gitolite.rc`. It **must** have **all** + the variables defined in `conf/example.gitolite.rc` (the "new" rc file), + because the new versions of the programs will be depending on seeing these + variables. + + However, it must also retain any customisations you made to the **old** + variables. + + So this is what you do: + + * make a copy of `conf/example.gitolite.rc` as `~/glrc.new` + * if your current `~/.gitolite.rc` had any customisations (where you + changed the defaults in some way), edit `~/glrc.new` and make those + same changes there + +3. upgrade the rc file first + + cp ~/glrc.new ~/.gitolite.rc + +4. upgrade the software + + src/install.pl + +And you're done. + +### upgrade notes for specific versions + +If any extra steps beyond the generic ones above are needed, they will be +listed here, newest first. + +#### upgrading from abb4580 + +Two new features (personal branches, and customisable logfile names/locations) +have been added between abb4580 and this version. + + * if you want to enable the personal branches feature, choose one of the + alternative values given for `$PERSONAL` or change it to something you + like; by default it is empty, which disables the feature + + * if you want the log files named or grouped differently, choose one of the + alternative values for `$GL_LOGT`. **Note** that if you choose to put + them in some other directory than the default, you **must** create that + directory (`mkdir`) yourself; gitolite will not do that for you diff --git a/src/gl-auth-command b/src/gl-auth-command index 2ea2dbb..6a8632c 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -23,9 +23,8 @@ use warnings; # common definitions # ---------------------------------------------------------------------------- -our $GL_ADMINDIR; -our $GL_CONF_COMPILED; -our $REPO_BASE; + +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE); our %repos; my $glrc = $ENV{HOME} . "/.gitolite.rc"; @@ -89,18 +88,36 @@ die "$perm access for $repo denied to $user" or $repos{$repo}{$perm}{'@all'}; # ---------------------------------------------------------------------------- -# over to git now +# logging, timestamp. also setup env vars for later # ---------------------------------------------------------------------------- -# ( but first save the reponame; we can save some time later in the hook ) +# reponame $ENV{GL_REPO}=$repo; +# timestamp +my ($s, $min, $h, $d, $m, $y) = (localtime)[0..5]; +$y += 1900; $m++; # usual adjustments +for ($s, $min, $h, $d, $m) { + $_ = "0$_" if $_ < 10; +} +$ENV{GL_TS} = "$y-$m-$d.$h:$min:$s"; + +# substitute template parameters and set the logfile name +$GL_LOGT =~ s/%y/$y/g; +$GL_LOGT =~ s/%m/$m/g; +$GL_LOGT =~ s/%d/$d/g; +$ENV{GL_LOG} = $GL_LOGT; + # if log failure isn't important enough to block access, get rid of all the # error checking -open my $log_fh, ">>", "$GL_ADMINDIR/log" +open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!"; -print $log_fh "\n", scalar(localtime), " $ENV{SSH_ORIGINAL_COMMAND} $user\n"; +print $log_fh "$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n"; close $log_fh or die "close log failed: $!"; +# ---------------------------------------------------------------------------- +# over to git now +# ---------------------------------------------------------------------------- + $repo = "'$REPO_BASE/$repo.git'"; exec("git", "shell", "-c", "$verb $repo"); diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 0907655..388d933 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -41,11 +41,7 @@ use Data::Dumper; # common definitions # ---------------------------------------------------------------------------- -our $GL_ADMINDIR; -our $GL_CONF; -our $GL_KEYDIR; -our $GL_CONF_COMPILED; -our $REPO_BASE; +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE); my $glrc = $ENV{HOME} . "/.gitolite.rc"; die "parse $glrc failed: " . ($! or $@) unless do $glrc; diff --git a/src/install.pl b/src/install.pl index 4624a74..6586ef2 100755 --- a/src/install.pl +++ b/src/install.pl @@ -3,9 +3,7 @@ use strict; use warnings; -our $REPO_BASE; -our $GL_ADMINDIR; -our $GL_CONF; +our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF); # wrapper around mkdir; it's not an error if the directory exists, but it is # an error if it doesn't exist and we can't create it @@ -31,10 +29,11 @@ unless (-f $glrc) { die "parse $glrc failed: " . ($! or $@) unless do $glrc; # mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist -wrap_mkdir( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +wrap_mkdir($repo_base_abs); wrap_mkdir($GL_ADMINDIR); # mkdir $GL_ADMINDIR's subdirs -for my $dir qw(conf doc keydir src) { +for my $dir qw(conf doc keydir logs src) { wrap_mkdir("$GL_ADMINDIR/$dir"); } @@ -54,13 +53,9 @@ EOF # finally, any potential changes to src/update-hook.pl must be propagated to # all the repos' hook directories -my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); -# err, no need to get all worked up if you can't CD there -- this may be the -# very first run and it hasn't been created yet -if (chdir("$repo_base_abs")) { - for my $repo (`find . -type d -name "*.git"`) { - chomp ($repo); - system("cp $GL_ADMINDIR/src/update-hook.pl $repo/hooks/update"); - chmod 0755, "$repo/hooks/update"; - } +chdir("$repo_base_abs") or die "chdir $repo_base_abs failed: $!\n"; +for my $repo (`find . -type d -name "*.git"`) { + chomp ($repo); + system("cp $GL_ADMINDIR/src/update-hook.pl $repo/hooks/update"); + chmod 0755, "$repo/hooks/update"; } diff --git a/src/update-hook.pl b/src/update-hook.pl index 41d6f76..fe92ec7 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -25,9 +25,7 @@ use warnings; # common definitions # ---------------------------------------------------------------------------- -our $GL_ADMINDIR; -our $GL_CONF_COMPILED; -our $PERSONAL; +our ($GL_CONF_COMPILED, $PERSONAL); our %repos; my $glrc = $ENV{HOME} . "/.gitolite.rc"; @@ -70,9 +68,11 @@ for my $refex (@allowed_refs) { # if log failure isn't important enough to block pushes, get rid of # all the error checking - open my $log_fh, ">>", "$GL_ADMINDIR/log" + open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!"; - print $log_fh "$perm: $ENV{GL_USER} $ENV{GL_REPO} $ref $oldsha $newsha\n"; + print $log_fh "$ENV{GL_TS} $perm\t" . + substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . + "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\n"; close $log_fh or die "close log failed: $!"; exit 0; } From 7abc629d51a21886c93221083868e8c2bdc7042d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 10 Sep 2009 15:57:52 +0530 Subject: [PATCH 055/850] faq-tips doc: "compile" as a separate step vindicated :-) it seems gitosis silently ignores config errors. It can't do anything else, considering *when* the config file is parsed (on every access!) --- doc/3-faq-tips-etc.mkd | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index ea3c3a7..4fc44b1 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -4,6 +4,7 @@ In this document: * errors, warnings, notes... * differences from gitosis + * error checking the config file * one user, many keys * who am I? * cool ideas I want feedback on @@ -34,6 +35,16 @@ Apart from the big ones listed in the top level README, and subjective ones like "better config file format", there are some small, but significant and concrete, differences from gitosis. +#### error checking the config file + +gitosis does not do any. I just found out that if you mis-spell `members` as +`member`, gitosis will silently ignore it, and leave you wondering why access +was denied. + +In gitolite, you have to "compile" the config file first (this step takes the +place of the commit+push in gitosis), and keyword typos *are* caught so you +know right away. + #### built-in logging ...just in case of emergency :-) From 3d44b003c844e48f58e84c08d13fd998e50b7e59 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 10 Sep 2009 19:05:07 +0530 Subject: [PATCH 056/850] clarifications in various messages etc (thanks to SethX for feedback) - install: a little more verbosity in the mkdir - install and example conf: some of the help text made more clear - auth: error message on bad $cmd is now clearer, plus no perl-warnings to confuse people --- conf/example.conf | 3 +++ src/gl-auth-command | 8 +++----- src/install.pl | 9 +++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index f19a95a..8f018ee 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -40,6 +40,9 @@ # notes: +# - the reponame is a simple name. Do not add the ".git" extension -- +# that will be added by the program when the actual repo is created + # - RW+ means non-ff push is allowed # - you can't write just "W" or "+"; it has to be R, or RW, or RW+ diff --git a/src/gl-auth-command b/src/gl-auth-command index 6a8632c..4fc757d 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -70,11 +70,9 @@ die "$cmd??? you're a funny guy..." # including the single quotes my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*).git'/); -die "$verb? I don't do odd jobs, sorry..." - unless $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS; - -die "I don't like the look of $repo, sorry!" - unless $repo =~ $REPONAME_PATT; +die "Sorry, I don't like the command you gave me: $cmd\n" + unless ( ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) + and $repo =~ $REPONAME_PATT ); # ---------------------------------------------------------------------------- # first level permissions check diff --git a/src/install.pl b/src/install.pl index 6586ef2..85b8607 100755 --- a/src/install.pl +++ b/src/install.pl @@ -10,7 +10,12 @@ our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF); sub wrap_mkdir { my $dir = shift; - -d $dir or mkdir($dir) or die "mkdir $dir failed: $!\n"; + if ( -d $dir ) { + print STDERR "$dir already exists\n"; + return; + } + mkdir($dir) or die "mkdir $dir failed: $!\n"; + print STDERR "created $dir\n"; } # the only path that is *fixed* (can't be changed without changing all 3 @@ -21,7 +26,7 @@ unless (-f $glrc) { # doesn't exist. Copy it across, tell user to edit it and come back system("cp conf/example.gitolite.rc $glrc"); print STDERR "created $glrc\n"; - print STDERR "please edit it, set the paths as you like, and rerun this script\n"; + print STDERR "please edit it, change the paths if you wish to, and RERUN THIS SCRIPT\n"; exit; } From 694050d6c4daef1c176d6361d1b02b63c5c9c16a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 10 Sep 2009 21:24:58 +0530 Subject: [PATCH 057/850] all src: suffixed a \n to all die's; error output looks cleaner now --- src/conf-convert.pl | 2 +- src/gl-auth-command | 10 +++++----- src/gl-compile-conf | 12 ++++++------ src/update-hook.pl | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/conf-convert.pl b/src/conf-convert.pl index f9775cb..c5b1ac1 100755 --- a/src/conf-convert.pl +++ b/src/conf-convert.pl @@ -22,7 +22,7 @@ my $groupname; # a gitosis.conf stanza ends when a new "[group name]" line shows up, so you # can't write as you go; you have to accumulate and flush sub flush { - die "repos but no users?" if (not @users and (@repos or @RO_repos)); + die "repos but no users?\n" if (not @users and (@repos or @RO_repos)); # just a groupname if (@users and not (@repos or @RO_repos)) { print "\@$groupname = ", join(" ", @users), "\n"; diff --git a/src/gl-auth-command b/src/gl-auth-command index 4fc757d..c301415 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -54,11 +54,11 @@ my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! # that in the message so people saying "ssh git@server" can see which gitosis # user he is being recognised as my $cmd = $ENV{SSH_ORIGINAL_COMMAND} - or die "no SSH_ORIGINAL_COMMAND? I'm not a shell, $user!"; + or die "no SSH_ORIGINAL_COMMAND? I'm not a shell, $user!\n"; # this check is largely for comic value if someone tries something outrageous; # $cmd gets split and the pieces examined more thoroughly later anyway -die "$cmd??? you're a funny guy..." +die "$cmd??? you're a funny guy...\n" if $cmd =~ /[<>&|;\n]/; # split into command and arguments; the pattern allows old style as well as @@ -81,7 +81,7 @@ die "Sorry, I don't like the command you gave me: $cmd\n" # we know the user and repo; we just need to know what perm he's trying my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W'); -die "$perm access for $repo denied to $user" +die "$perm access for $repo denied to $user\n" unless $repos{$repo}{$perm}{$user} or $repos{$repo}{$perm}{'@all'}; @@ -109,9 +109,9 @@ $ENV{GL_LOG} = $GL_LOGT; # if log failure isn't important enough to block access, get rid of all the # error checking open my $log_fh, ">>", $ENV{GL_LOG} - or die "open log failed: $!"; + or die "open log failed: $!\n"; print $log_fh "$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n"; -close $log_fh or die "close log failed: $!"; +close $log_fh or die "close log failed: $!\n"; # ---------------------------------------------------------------------------- # over to git now diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 388d933..ce8a0b9 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -84,7 +84,7 @@ sub expand_userlist die "bad user $item\n" unless $item =~ $USERNAME_PATT; if ($item =~ /^@/) # nested group { - die "undefined group $item" unless $groups{$item}; + die "undefined group $item\n" unless $groups{$item}; # add those names to the list push @new_list, @{ $groups{$item} }; } @@ -167,7 +167,7 @@ while (<$conf_fh>) my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]); -close $compiled_fh or die "close compiled-conf failed: $!"; +close $compiled_fh or die "close compiled-conf failed: $!\n"; # ---------------------------------------------------------------------------- # any new repos created? @@ -185,7 +185,7 @@ for my $repo (keys %repos) { unless (-d "$repo.git") { - mkdir("$repo.git") or die "mkdir $repo.git failed: $!"; + mkdir("$repo.git") or die "mkdir $repo.git failed: $!\n"; wrap_chdir("$repo.git"); system("git init --bare"); system("cp $GL_ADMINDIR/src/update-hook.pl hooks/update"); @@ -217,7 +217,7 @@ for my $pubkey (glob("*.pub")) print $newkeys_fh `cat $pubkey`; } print $newkeys_fh "# gitolite end\n"; -close $newkeys_fh or die "close newkeys failed: $!"; +close $newkeys_fh or die "close newkeys failed: $!\n"; # all done; overwrite the file (use cat to avoid perm changes) system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); @@ -234,9 +234,9 @@ if (-d ".git") if (system("git diff --cached --quiet") ) { open my $commit_ph, "|-", "git commit -F -" - or die "open commit failed: $!"; + or die "open commit failed: $!\n"; print $commit_ph "keydir changed\n\n"; print $commit_ph `git diff --cached --name-status`; - close $commit_ph or die "close commit failed: $!"; + close $commit_ph or die "close commit failed: $!\n"; } } diff --git a/src/update-hook.pl b/src/update-hook.pl index fe92ec7..c703723 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -69,11 +69,11 @@ for my $refex (@allowed_refs) # if log failure isn't important enough to block pushes, get rid of # all the error checking open my $log_fh, ">>", $ENV{GL_LOG} - or die "open log failed: $!"; + or die "open log failed: $!\n"; print $log_fh "$ENV{GL_TS} $perm\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\n"; - close $log_fh or die "close log failed: $!"; + close $log_fh or die "close log failed: $!\n"; exit 0; } } From d9d432a483ffc3c8e7228713bef7cc8de9c74829 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 11 Sep 2009 23:03:41 +0530 Subject: [PATCH 058/850] faq/tips: added "common errors..." section with 2 examples --- doc/3-faq-tips-etc.mkd | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 4fc44b1..2f65fa2 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -2,7 +2,8 @@ In this document: - * errors, warnings, notes... + * common errors and mistakes + * other errors, warnings, notes... * differences from gitosis * error checking the config file * one user, many keys @@ -10,7 +11,20 @@ In this document: * cool ideas I want feedback on * developer specific branches -### errors, warnings, notes... +### common errors and mistakes + + * forgetting to suffix `.git` to the end of the reponame in the `git clone`. + This suffix is *not* used in the gitolite config file for the sake of + clarity and cleaner syntax, but don't let that fool you. It's a + convention in the git world that **bare repos** end with `.git`. + + * adding `repositories/` at the start of the repo name in the `git clone`. + This error is typically made by the *admin* himself -- because he knows + what `$REPO_BASE` is set to and thinks he has to provide that prefix on + the client side also :-) In fact gitolite prepends `$REPO_BASE` when it + is required anyway, so you shouldn't do the same thing! + +### other errors, warnings, notes... * cloning an empty repo is only possible with clients greater than 1.6.2. So at least one of your clients needs to have a recent git. Once at least From 7f9c2e6510c396007baf03dce0b809cd097bfbf3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 12 Sep 2009 19:38:11 +0530 Subject: [PATCH 059/850] minor doc updates - README: re not needing root access - doc/3: "empty clone error" vis-a-vis git 1.6.4.3 --- README.mkd | 1 + doc/3-faq-tips-etc.mkd | 1 + 2 files changed, 2 insertions(+) diff --git a/README.mkd b/README.mkd index a49eb67..fa8e2f4 100644 --- a/README.mkd +++ b/README.mkd @@ -28,6 +28,7 @@ a typical $DAYJOB setting, there are some issues: and be done * often, "python-setuptools" isn't installed (and on a Solaris9 I was trying to help remotely, we never did manage to install it eventually) + * or you don't have root access, or the ability to add users * the most requested feature (see "what's new?") had to be written anyway ### what's gone diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 2f65fa2..9623149 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -33,6 +33,7 @@ In this document: * when you clone an empty repo, git seems to complain about the remote hanging up or something. I have no idea what that is, but it doesn't seem to hurt anything. This happens even in normal git, not just gitolite. + [Update 2009-09-14; this has been fixed in git 1.6.4.3] * once in a while, if you're feeling particularly BOFH-ish, take a look at `$GL_ADMINDIR/log` :-) From 2ca491662151fd486395871284431298a7dedfe3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 14 Sep 2009 12:31:19 +0530 Subject: [PATCH 060/850] doc/3: explain how 2-level access checks affect personal branch rights --- doc/3-faq-tips-etc.mkd | 58 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 9623149..1a5e426 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -5,6 +5,7 @@ In this document: * common errors and mistakes * other errors, warnings, notes... * differences from gitosis + * two levels of access rights checking * error checking the config file * one user, many keys * who am I? @@ -50,6 +51,34 @@ Apart from the big ones listed in the top level README, and subjective ones like "better config file format", there are some small, but significant and concrete, differences from gitosis. +#### two levels of access rights checking + +Gitolite has two levels of access checks. The **first check** is what I will +call the **pre-git** level (this is the only check that gitosis has). At this +stage, the `gl-auth-command` has been invoked by `sshd`, and it knows just +three things: + + * who, + * what repository, and + * what type of access (R or W) + +Note that at this point no git program has entered the picture, and we have no +way of knowing what **ref** (branch, tag, etc) he is trying to update, even if +it is a "write" operation. + +For a "read" operation to pass this check, the username (or `@all`) must be +mentioned on some line in the config for this repo. + +For a "write" operation, there is an additional restriction: lines specifying +only `R` (read access) don't count. *The user must have write access to +**some** ref in the repo in order to pass this stage!* + +The **second check** is via a git `update hook`. This check only happens for +write operations. By this time we know what "ref" he is trying to update, as +well as the old and the new SHAs of that ref (by which we can also deduce +whether it's a fast forward or not). This is where the "per-branch" +permissions come into play. + #### error checking the config file gitosis does not do any. I just found out that if you mis-spell `members` as @@ -147,6 +176,31 @@ namespace, and each developer controls his/her own set of branches! The amount of code needed? *One line!* I'll spend about 3x more on declaring and initialising the new variable, and 30x more on documenting it :-) -**That** is how neatly gitolite is designed... +**Note that a user who has NO write access cannot have personal branches**; if +you read the section (above) on "two levels of access rights checking" you'll +understand why. -/me pats himself on the back ;-) +For instance, in the following example, `user3` cannot push to any +`refs/heads/personal/user3/*` branches because the first level check stops him +cold: + + # assume $PERSONAL = 'refs/heads/personal' in ~/.gitolite.rc + repo myrepo + RW+ master = sitaram + RW+ release = qa_guy + RW = user1 user2 + R = user3 + +If we relax that check, *any* access becomes *write* access. Yes it will be +caught later, by the hook, but it's good practice to catch things in multiple +places. + +If you want `user3` to have his own personal branch, but without write access +to any of the "real" branches (like "master", "release", etc.), just use a +dummy branch. Choose a name that will never exist in practice, or even if +someone creates it, we don't care. For example, this will get him past the +first check: + + RW dummy = user3 + +Just don't *show* the user this config file; it might sound insulting :-) From 5758f69a4326644af1830d71ca3cd8f53a950cb4 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 15 Sep 2009 12:04:15 +0530 Subject: [PATCH 061/850] doc: added 4-push-to-admin --- doc/3-faq-tips-etc.mkd | 8 +++++ doc/4-push-to-admin.mkd | 79 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 doc/4-push-to-admin.mkd diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 1a5e426..4347f3f 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -45,6 +45,14 @@ In this document: the "compile" script will just `mkdir a/b/c.git`, assuming "a/b" has already been created + * if you run `git init` inside `$GL_ADMINDIR` (that is, make it a normal, + non-bare, repo), then, everytime you "compile" (run + `src/gl-compile-conf`), any changes to `conf` and `keydir` will + automatically be committed. This is a simple safety net in case you + accidentally delete the whole config or something. Also see + [4-push-to-admin.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/4-push-to-admin.mkd) + if you really know what you're doing and want "push to admin". + ### differences from gitosis Apart from the big ones listed in the top level README, and subjective ones diff --git a/doc/4-push-to-admin.mkd b/doc/4-push-to-admin.mkd new file mode 100644 index 0000000..ef5125b --- /dev/null +++ b/doc/4-push-to-admin.mkd @@ -0,0 +1,79 @@ +# "push to admin" in gitolite + +---- + +Gitosis's default mode of admin is by cloning and pushing the `gitosis-admin` +repo. I call this "push to admin". It's a very cool/cute feature, and I +loved it at first. + +But it's a ***support nightmare***. Half the gitosis angst on `#git` is +because of this feature. Gitolite does not use or endorse this method for +people new to git, or ssh or (worse) both. + +However, ***if*** you know git and ssh really, *really*, well and you know +what you're doing, this is a pretty nice thing to have -- does make life +easier, I admit. + +So, here is how to make PTA (hey nice acronym, just missing an "I") work on +gitolite as well. But remember, there is NO SUPPORT! Go away. Leave me +alone... Anything else on gitolite I will help but not this, ok? :-) + +The instructions are presented as shell commands; they should be fairly +obvious. All paths are from the default `~/.gitolite.rc`; if you changed any, +make the same changes below. + +---- + +First, on the server, log on to the `git` userid, add a new repo called +`gitolite-admin` to the config file, give yourself `RW` or `RW+` rights to it, +and "compile": + + cd ~/.gitolite + vim conf/gitolite.conf # add gitolite-admin repo, etc + src/gl-compile-conf + +Now, if you look at the "compile" script, it has an *automatic* local commit +inside, just for safety, which kicks in every time you compile. This only +works if it finds a ".git" directory, and it was designed as an "automatic +backup/safety net" type of thing, in case I accidentally deleted the whole +config file or something. + +We need to disable this, because now we have a *better* repo, one that is +manually pushed, and presumably has proper commit messages! + + mv .git .disable.git # yeah it's a hack, sue me + +Now the compile command created an empty, bare, "gitolite-admin" repo, so we +seed it with the current contents of the config and keys. (A note on the +`GIT_WORK_TREE` variable: I avoid setting these variables in the normal way +because I always forget to unset them later, and then when I `cd` to other +repos they play havoc with my git commands, so this is how I do it) + + cd ~/repositories/gitolite-admin.git + GIT_WORK_TREE=/home/git/.gitolite git add conf/gitolite.conf keydir + GIT_WORK_TREE=/home/git/.gitolite git commit -am start + +Now we have to setup the post-update hook for push-to-admin to work. The +hook should (1) make a forced checkout in the "live" config directory (which +is `~/.gitolite`), and (2) run the compile script. So we create a hook with +the appropriate code in it, and then make it executable + + cat < hooks/post-update + #!/bin/sh + + GIT_WORK_TREE=/home/git/.gitolite git checkout -f + + cd /home/git/.gitolite + src/gl-compile-conf + EOFPU + + chmod +x hooks/post-update + +---- + +Now get to your workstation, and + + git clone git@server:gitolite-admin.git + +That's it, we're done. You're in gitosis land as far as this is concerned +now. So knock yourself out. Or lock yourself out... :-) From f54c6c7a525937b3087742c593a9bab43af51406 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 15 Sep 2009 21:02:23 +0530 Subject: [PATCH 062/850] compile: make error messages grab the admin's attention required if you do "push to admin" --- src/gl-compile-conf | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index ce8a0b9..fd18f20 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -43,8 +43,13 @@ use Data::Dumper; our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE); +# now that this thing *may* be run via "push to admin", any errors have to +# grab the admin's ATTENTION so he won't miss them among the other messages a +# typical push generates +my $ATTN = "\n\t\t***** ERROR ***** "; + my $glrc = $ENV{HOME} . "/.gitolite.rc"; -die "parse $glrc failed: " . ($! or $@) unless do $glrc; +die "$ATTN parse $glrc failed: " . ($! or $@) unless do $glrc; # ---------------------------------------------------------------------------- # definitions specific to this program @@ -66,11 +71,11 @@ umask(0077); # ---------------------------------------------------------------------------- sub wrap_chdir { - chdir($_[0]) or die "chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; + chdir($_[0]) or die "$ATTN chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; } sub wrap_open { - open (my $fh, $_[0], $_[1]) or die "open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; + open (my $fh, $_[0], $_[1]) or die "$ATTN open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; return $fh; } @@ -81,10 +86,10 @@ sub expand_userlist for my $item (@list) { - die "bad user $item\n" unless $item =~ $USERNAME_PATT; + die "$ATTN bad user $item\n" unless $item =~ $USERNAME_PATT; if ($item =~ /^@/) # nested group { - die "undefined group $item\n" unless $groups{$item}; + die "$ATTN undefined group $item\n" unless $groups{$item}; # add those names to the list push @new_list, @{ $groups{$item} }; } @@ -122,7 +127,7 @@ while (<$conf_fh>) if (/^(@\S+) = (.*)/) { push @{ $groups{$1} }, expand_userlist( split(' ', $2) ); - die "bad group $1\n" unless $1 =~ $USERNAME_PATT; + die "$ATTN bad group $1\n" unless $1 =~ $USERNAME_PATT; } # repo(s) elsif (/^repo (.*)/) @@ -161,13 +166,13 @@ while (<$conf_fh>) } else { - die "can't make head or tail of '$_'\n"; + die "$ATTN can't make head or tail of '$_'\n"; } } my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]); -close $compiled_fh or die "close compiled-conf failed: $!\n"; +close $compiled_fh or die "$ATTN close compiled-conf failed: $!\n"; # ---------------------------------------------------------------------------- # any new repos created? @@ -185,7 +190,7 @@ for my $repo (keys %repos) { unless (-d "$repo.git") { - mkdir("$repo.git") or die "mkdir $repo.git failed: $!\n"; + mkdir("$repo.git") or die "$ATTN mkdir $repo.git failed: $!\n"; wrap_chdir("$repo.git"); system("git init --bare"); system("cp $GL_ADMINDIR/src/update-hook.pl hooks/update"); @@ -217,7 +222,7 @@ for my $pubkey (glob("*.pub")) print $newkeys_fh `cat $pubkey`; } print $newkeys_fh "# gitolite end\n"; -close $newkeys_fh or die "close newkeys failed: $!\n"; +close $newkeys_fh or die "$ATTN close newkeys failed: $!\n"; # all done; overwrite the file (use cat to avoid perm changes) system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); @@ -234,9 +239,9 @@ if (-d ".git") if (system("git diff --cached --quiet") ) { open my $commit_ph, "|-", "git commit -F -" - or die "open commit failed: $!\n"; + or die "$ATTN open commit failed: $!\n"; print $commit_ph "keydir changed\n\n"; print $commit_ph `git diff --cached --name-status`; - close $commit_ph or die "close commit failed: $!\n"; + close $commit_ph or die "$ATTN close commit failed: $!\n"; } } From fde9708cbfbdfa04c17ed923f7e00db1a20c12ed Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 17 Sep 2009 10:39:13 +0530 Subject: [PATCH 063/850] compile: better message when authkeys absent for security reasons, we refuse to create ~/.ssh/authorized_keys if it doesn't exist. Explain this better and point to the documentation --- doc/2-admin.mkd | 4 +++- src/gl-compile-conf | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index ae65b72..028b23a 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -29,7 +29,9 @@ familiar with ssh and authkeys etc. If so, please read up at least [this](http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh), and preferably also the man pages for sshd and sshd\_config, to make - sure you understand the security implications of what you are doing + sure you understand the security implications of what you are doing. + Once you have understood that, create at least an empty + `~/.ssh/authorized_keys` file before proceeding to the next step * cd to `$GL_ADMINDIR` and run `src/gl-compile-conf` diff --git a/src/gl-compile-conf b/src/gl-compile-conf index fd18f20..69bbb75 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -46,7 +46,7 @@ our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE); # now that this thing *may* be run via "push to admin", any errors have to # grab the admin's ATTENTION so he won't miss them among the other messages a # typical push generates -my $ATTN = "\n\t\t***** ERROR ***** "; +my $ATTN = "\n\t\t***** ERROR *****\n "; my $glrc = $ENV{HOME} . "/.gitolite.rc"; die "$ATTN parse $glrc failed: " . ($! or $@) unless do $glrc; @@ -75,7 +75,8 @@ sub wrap_chdir { } sub wrap_open { - open (my $fh, $_[0], $_[1]) or die "$ATTN open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; + open (my $fh, $_[0], $_[1]) or die "$ATTN open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n" . + ( $_[2] || '' ); # suffix custom error message if given return $fh; } @@ -203,7 +204,9 @@ for my $repo (keys %repos) # "compile" ssh authorized_keys # ---------------------------------------------------------------------------- -my $authkeys_fh = wrap_open( "<", $ENV{HOME} . "/.ssh/authorized_keys" ); +my $authkeys_fh = wrap_open( "<", $ENV{HOME} . "/.ssh/authorized_keys", + "\tFor security reasons, gitolite will not *create* this file if it does\n" . + "\tnot already exist. Please see the \"admin\" document for details\n"); my $newkeys_fh = wrap_open( ">", $ENV{HOME} . "/.ssh/new_authkeys" ); # save existing authkeys minus the GL-added stuff while (<$authkeys_fh>) From 86faae4d4cf93d050490eaee6379c2eb0385410b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 15 Sep 2009 21:07:00 +0530 Subject: [PATCH 064/850] compile+conf: allow lists (@listname) for reponames too why should just usernames have all the fun :) The "expand_userlist" function is now "expand_list" and serves generically. The example conf has also been updated correspondingly --- conf/example.conf | 28 ++++++++++++++++++---------- doc/0-UPGRADE.mkd | 7 +++++++ src/gl-compile-conf | 25 +++++++++++++++++-------- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 8f018ee..86fdeca 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -4,6 +4,7 @@ # - everything in this is space-separated; no commas, semicolons, etc # - comments in the normal shell-ish style; no surprises there # - there are no continuation lines of any kind +# - user/repo names as simple as possible # objectives, over and above gitosis: # - simpler syntax @@ -12,17 +13,19 @@ # - allows branch level control # ---------------------------------------------------------------------------- -# USERS and GROUPS +# LISTS # syntax: -# @groupname = username [...] +# @listname = name [...] +# lists can be used as shorthand for usernames as well as reponames -# usernames and groupnames should be as simple as possible - -# too many users in one group? just add more such lines -# (they accumulate, like squid ACLs) +# a list is equivalent to typing out all the right hand side names, so why do +# we need lists at all? (1) to be able to reuse the same set of usernames in +# the paras for different repos, (2) to keep the lines short, because lists +# accumulate, like squid ACLs, so you can say: @cust_A = cust1 cust2 @cust_A = cust99 +# and this is the same as listing all three on the same line # you can nest groups, but not recursively of course! @interns = indy james @@ -31,11 +34,15 @@ @staff = me alice @secret_staff = bruce whitfield martin +@pubrepos = linux git + +@privrepos = supersecretrepo anothersecretrepo + # ---------------------------------------------------------------------------- # REPOS, REFS, and PERMISSIONS # syntax: -# repo [one or more reponames] +# repo [one or more repos] # (R|RW|RW+) [zero or more refnames] = [one or more users] # notes: @@ -51,8 +58,9 @@ # - prefixed by "refs/heads/" if it doesn't start with "refs/" # (i.e., tags have to be explicitly named as refs/tags/pattern) -# - the list of users can inlude any group name defined earlier -# - "@all" is a special, predefined, groupname +# - the list of users or repos can inlude any group name defined earlier +# - "@all" is a special, predefined, groupname that means "all users" +# (there is no corresponding shortcut for all repos) # anyone can play in the sandbox, including making non-fastforward commits # (that's what the "+" means) @@ -72,7 +80,7 @@ repo cust_A_repo # idea for the tags syntax shamelessly copied from git.git # Documentation/howto/update-hook-example.txt :) -repo secret +repo @privrepos thirdsecretrepo RW+ pu = bruce RW master next = bruce RW refs/tags/v[0-9].* = bruce diff --git a/doc/0-UPGRADE.mkd b/doc/0-UPGRADE.mkd index 4d55ac0..9b0d9a5 100644 --- a/doc/0-UPGRADE.mkd +++ b/doc/0-UPGRADE.mkd @@ -37,6 +37,13 @@ And you're done. If any extra steps beyond the generic ones above are needed, they will be listed here, newest first. +#### upgrading from 5758f69 + +Between 5758f69 and this version, gitolite learnt to allow "groupnames" for +repos as well. The `conf/example.conf` has been recommented to explain the +syntax but it's really a no-brainer: what you could previously do only for +usernames, you can now do for reponames also. + #### upgrading from abb4580 Two new features (personal branches, and customisable logfile names/locations) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 69bbb75..d8a583b 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -58,8 +58,11 @@ die "$ATTN parse $glrc failed: " . ($! or $@) unless do $glrc; # command and options for authorized_keys my $AUTH_COMMAND="$GL_ADMINDIR/src/gl-auth-command"; my $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; -my $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern +# note that REPONAME_PATT allows a "/" also, which USERNAME_PATT doesn't +my $REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern +my $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern +# groups can now represent user groups or repo groups my %groups = (); my %repos = (); @@ -80,14 +83,16 @@ sub wrap_open { return $fh; } -sub expand_userlist +sub expand_list { my @list = @_; my @new_list = (); for my $item (@list) { - die "$ATTN bad user $item\n" unless $item =~ $USERNAME_PATT; + # we test with the slightly more relaxed pattern here; we'll catch the + # "/" in user name thing later; it doesn't affect security anyway + die "$ATTN bad user or repo name $item\n" unless $item =~ $REPONAME_PATT; if ($item =~ /^@/) # nested group { die "$ATTN undefined group $item\n" unless $groups{$item}; @@ -124,16 +129,19 @@ while (<$conf_fh>) # and blank lines next unless /\S/; - # user groups + # user or repo groups if (/^(@\S+) = (.*)/) { - push @{ $groups{$1} }, expand_userlist( split(' ', $2) ); - die "$ATTN bad group $1\n" unless $1 =~ $USERNAME_PATT; + push @{ $groups{$1} }, expand_list( split(' ', $2) ); + # again, we take the more "relaxed" pattern + die "$ATTN bad group $1\n" unless $1 =~ $REPONAME_PATT; } # repo(s) elsif (/^repo (.*)/) { - @repos = split(' ', $1); + # grab the list and expand any @stuff in it + @repos = split ' ', $1; + @repos = expand_list ( @repos ); } # actual permission line elsif (/^(R|RW|RW\+) (.* )?= (.+)/) @@ -150,8 +158,9 @@ while (<$conf_fh>) @refs = map { m(^refs/) or s(^)(refs/heads/); $_ } @refs; # expand the user list, unless it is just "@all" - @users = expand_userlist ( @users ) + @users = expand_list ( @users ) unless (@users == 1 and $users[0] eq '@all'); + do { die "$ATTN bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; # ok, we can finally populate the %repos hash for my $repo (@repos) # each repo in the current stanza From 838dd65d5fafbff5b2fcd5c447830f5ec098ed88 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 21 Sep 2009 07:48:30 +0530 Subject: [PATCH 065/850] compile+doc/3: deal with older gits - detect/warn git version < 1.6.2 - create documentation with details on client-side workaround - change the "git init --bare" to (older) "git --bare init", since the old syntax still works anyway --- doc/3-faq-tips-etc.mkd | 30 ++++++++++++++++++++++++++++++ src/gl-compile-conf | 15 ++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 4347f3f..c31f4da 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -3,6 +3,7 @@ In this document: * common errors and mistakes + * git version dependency * other errors, warnings, notes... * differences from gitosis * two levels of access rights checking @@ -25,6 +26,35 @@ In this document: the client side also :-) In fact gitolite prepends `$REPO_BASE` when it is required anyway, so you shouldn't do the same thing! +### git version dependency + +Here's a workaround for a version dependency that the normal flow of gitolite +has. + +When you edit your config file to create a new repo, and run +`src/gl-compile-conf`, gitolite creates an empty, bare repo for you. +Normally, you're expected to clone this on the client side, and start working +-- make your first commit(s), then push, etc. + +However, cloning an empty repo requires a server side git version that is at +least 1.6.2. Gitolite detects this when creating a repo, and warns you. + +The workaround is to use the older (gitosis-style) method on the client: +create an empty repo locally, make a commit or two, set an "origin" remote, +and then push. Something like: + + mkdir my-new-project + cd my-new-project + git init + git commit --allow-empty -m 'Initial repository' + # or, if your client side git is too old for --allow-empty, just make some + # files, "git add" them, then "git commit" + git remote add origin git@gitolite-server:my-new-project.git + git push origin master:master + +Once this is done, the repo is available for cloning by anyone else in the +normal way, since it's not empty anymore. + ### other errors, warnings, notes... * cloning an empty repo is only possible with clients greater than 1.6.2. diff --git a/src/gl-compile-conf b/src/gl-compile-conf index d8a583b..06917da 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -192,6 +192,13 @@ close $compiled_fh or die "$ATTN close compiled-conf failed: $!\n"; # did not have that luxury, so it was forced to detect the first push and # create it then +# but it turns out not everyone has "modern" gits :) +my $git_version = `git --version`; +my ($gv_maj, $gv_min, $gv_patchrel) = ($git_version =~ m/git version (\d+)\.(\d+)\.(\d+)/); +die "$ATTN I can't understand $git_version\n" unless ($gv_maj >= 1); +$git_version = $gv_maj*10000 + $gv_min*100 + $gv_patchrel; # now it's "normalised" +my $git_too_old = 0; + # repo-base needs to be an absolute path for this loop to work right # so if it was not already absolute, prefix $HOME. my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); @@ -202,12 +209,18 @@ for my $repo (keys %repos) { mkdir("$repo.git") or die "$ATTN mkdir $repo.git failed: $!\n"; wrap_chdir("$repo.git"); - system("git init --bare"); + system("git --bare init"); system("cp $GL_ADMINDIR/src/update-hook.pl hooks/update"); chmod 0755, "hooks/update"; wrap_chdir("$repo_base_abs"); + $git_too_old++ if $git_version < 10602; # that's 1.6.2 to you } } +warn "\n\t\t***** WARNING *****\n" . + "\tyour git version is older than 1.6.2\n" . + "\tgitolite will work but you MUST read the section on\n" . + "\t\"git version dependency\" in doc/3-faq-tips-etc.mkd\n" + if $git_too_old; # ---------------------------------------------------------------------------- # "compile" ssh authorized_keys From df3dd0de488ef8cfca0160dd82e4d86a8d1e41f2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 21 Sep 2009 14:41:37 +0530 Subject: [PATCH 066/850] compile, rc, doc/3: allow custom umask --- conf/example.gitolite.rc | 8 ++++++++ doc/3-faq-tips-etc.mkd | 7 +++++-- src/gl-compile-conf | 6 +++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index f5e64a1..d2adbfd 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -16,6 +16,14 @@ $REPO_BASE="repositories"; +# the default umask for repositories is 0077; change this if you run stuff +# like gitweb and find it can't read the repos. Please note the syntax; the +# leading 0 is required + +$REPO_UMASK = 0077; # gets you 'rwx------' +# $REPO_UMASK = 0027; # gets you 'rwxr-x---' +# $REPO_UMASK = 0022; # gets you 'rwxr-xr-x' + # -------------------------------------- # I see no reason anyone may want to change the gitolite admin directory, but diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index c31f4da..595d938 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -59,7 +59,7 @@ normal way, since it's not empty anymore. * cloning an empty repo is only possible with clients greater than 1.6.2. So at least one of your clients needs to have a recent git. Once at least - one commit has been made, older clients can also use it. + one commit has been made, older clients can also use it * when you clone an empty repo, git seems to complain about the remote hanging up or something. I have no idea what that is, but it doesn't seem @@ -81,7 +81,10 @@ normal way, since it's not empty anymore. automatically be committed. This is a simple safety net in case you accidentally delete the whole config or something. Also see [4-push-to-admin.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/4-push-to-admin.mkd) - if you really know what you're doing and want "push to admin". + if you really know what you're doing and want "push to admin" + + * gitweb not able to read your repos? You can change the umask for newly + created repos to something more relaxed -- see the `~/.gitolite.rc` file ### differences from gitosis diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 06917da..3154dc1 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -41,7 +41,7 @@ use Data::Dumper; # common definitions # ---------------------------------------------------------------------------- -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK); # now that this thing *may* be run via "push to admin", any errors have to # grab the admin's ATTENTION so he won't miss them among the other messages a @@ -66,8 +66,8 @@ my $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple patter my %groups = (); my %repos = (); -# set a restrictive umask, just in case -umask(0077); +# set the umask before creating any files +umask($REPO_UMASK); # ---------------------------------------------------------------------------- # subroutines From 4879a03c60353a65257add190fdbd2fddd7dd69c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 21 Sep 2009 19:01:47 +0530 Subject: [PATCH 067/850] Makefile wraps "git archive" to record "git describe" output in tar --- .gitignore | 4 ++++ Makefile | 16 ++++++++++++++++ doc/0-INSTALL.mkd | 14 ++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f60da1e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.tar +*.tgz +*.tar.gz +*.tar.bz2 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..730fa72 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +# this is a simple wrapper around "git archive" using make + +# "make [refname].tar" produces a tar of refname, then adds a file containing +# the "git describe" output for that refname to the tar. This lets you say +# "cat .GITOLITE-VERSION" to find out which ref produced this tar + +# Note: I'm not sure if that "-r" is a GNU tar extension... + +.GITOLITE-VERSION: + @touch .GITOLITE-VERSION + +%.tar: .GITOLITE-VERSION + git describe --all --long $* > .GITOLITE-VERSION + git archive $* > $@ + tar -r -f $@ .GITOLITE-VERSION + rm .GITOLITE-VERSION diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 10450a2..9d92ce9 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -13,9 +13,19 @@ A major objective is to allow use by people without root access, permissions to create other userids, etc. Even if you have root, please add a user just for gitolite and do all this from that user. -### quick install +### getting a tar file from a clone - * cd to the directory where you unpacked the source +You can clone the repo from github, then execute a make command to extract a +tar file of the branch you want. Please use the make command, not a plain +"git archive". The comments in the `Makefile` will explain why. + + git clone git://github.com/sitaramc/gitolite.git + make master.tar + # or maybe "make rebel.tar" or "make pu.tar" + +### quick install from tar file + + * make a temp directory somewhere, cd to it, and unpack the tar file * run `src/install.pl` and follow the prompts **When you are told to edit some file, please read the comments in the file**. From 780f636c0aba76d0f3a105c500a7a8ba7831bb40 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 16 Sep 2009 10:16:11 +0530 Subject: [PATCH 068/850] doc warnings: doc/admin: add warning about creating repos manually! doc/4: add warning on compile errors when using p-t-a --- doc/2-admin.mkd | 20 +++++++++++++++++--- doc/4-push-to-admin.mkd | 10 ++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 028b23a..0585e06 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -5,6 +5,14 @@ ### administer +First of all, ***do NOT add new repos manually***, unless you know how to add +the required hook as well. Without the hook, branch-level access control will +not work for that repo, which sorta defeats the idea of using gitolite :-) + +Please read on to see how to do this correctly. + +#### adding users and repos + * ask each user who will get access to send you a public key. See other sources (for example [here](http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key)) @@ -16,9 +24,15 @@ * copy all these `*.pub` files to `$GL_KEYDIR` - * edit the config file (`$GL_CONF`) and give the new users permissions as - required. The users names should be exactly the same as their keyfile - names, but without the `.pub` extension + * the config file (`$GL_CONF`) is very well commented, please take a couple + of minutes to read it. Then edit it and + + * add new repos as needed + * add new users and give them permissions as required. The users names + should be exactly the same as their keyfile names, but without the + `.pub` extension + +#### compiling * backup your `~/.ssh/authorized_keys` file if you feel nervous :-) * that's "backup" as in "copy", not "move". The next step won't work if diff --git a/doc/4-push-to-admin.mkd b/doc/4-push-to-admin.mkd index ef5125b..12a6bc3 100644 --- a/doc/4-push-to-admin.mkd +++ b/doc/4-push-to-admin.mkd @@ -22,6 +22,16 @@ The instructions are presented as shell commands; they should be fairly obvious. All paths are from the default `~/.gitolite.rc`; if you changed any, make the same changes below. +> ---- + +> **WARNING**: the "compilation" runs via a `post-update` hook. Which, by +> definition, runs *after* the push has successfully completed. As a +> result, a *compilation error* will be visible to the admin doing the `git +> push` but will not otherwise look like an error to client-side git (in +> terms of return codes, scripting, etc., or even the "git gui" if you +> happen to use that for pushing). So be sure to watch out for compile +> error messages on push when you do this. + ---- First, on the server, log on to the `git` userid, add a new repo called From 5415b425e765896971bafb66eae67ab928ac0868 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 16 Sep 2009 19:52:03 +0530 Subject: [PATCH 069/850] example conf, doc/3: explain refexes --- conf/example.conf | 20 +++++++++++++++----- doc/3-faq-tips-etc.mkd | 12 ++++++++---- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 86fdeca..14c6817 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -43,7 +43,7 @@ # syntax: # repo [one or more repos] -# (R|RW|RW+) [zero or more refnames] = [one or more users] +# (R|RW|RW+) [zero or more refexes] = [one or more users] # notes: @@ -53,15 +53,25 @@ # - RW+ means non-ff push is allowed # - you can't write just "W" or "+"; it has to be R, or RW, or RW+ -# - if no ref name appears, the rule applies to all refs in that repo -# - ref names are perl regex patterns -# - prefixed by "refs/heads/" if it doesn't start with "refs/" -# (i.e., tags have to be explicitly named as refs/tags/pattern) +# - a refex is a regex that matches a ref :-) If you see the examples +# below you'll get it easy enough + +# - refexes are specified in perl regex syntax +# - if no refex appears, the rule applies to all refs in that repo +# - a refex is automatically prefixed by "refs/heads/" if it doesn't start +# with "refs/" (so tags have to be explicitly named as +# refs/tags/pattern) # - the list of users or repos can inlude any group name defined earlier # - "@all" is a special, predefined, groupname that means "all users" # (there is no corresponding shortcut for all repos) +# matching: + +# - user, repo, and access (W or +) are known. For that combination, if +# any of the refexes match the refname being updated, the push succeeds. +# If none of them match, it fails + # anyone can play in the sandbox, including making non-fastforward commits # (that's what the "+" means) repo sandbox diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 595d938..465959c 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -10,7 +10,7 @@ In this document: * error checking the config file * one user, many keys * who am I? - * cool ideas I want feedback on + * other cool things * developer specific branches ### common errors and mistakes @@ -117,8 +117,12 @@ only `R` (read access) don't count. *The user must have write access to The **second check** is via a git `update hook`. This check only happens for write operations. By this time we know what "ref" he is trying to update, as well as the old and the new SHAs of that ref (by which we can also deduce -whether it's a fast forward or not). This is where the "per-branch" -permissions come into play. +whether it's a rewind or not). This is where the "per-branch" permissions +come into play. + +Each refex that allows `W` access (or `+` if this is a rewind) for *this* +user, on *this* repo, is matched against the actual refname being updated. If +any of the refexes match, the push succeeds. If none of them match, it fails. #### error checking the config file @@ -188,7 +192,7 @@ In gitolite, it's simple: just ask nicely :-) PTY allocation request failed on channel 0 no SSH_ORIGINAL_COMMAND? I'm not a shell, sitaram! -### cool ideas +### other cool things #### developer specific branches From 344723b9741239efc23757b82596c3b08549db7a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 16 Sep 2009 22:25:32 +0530 Subject: [PATCH 070/850] conf+doc/3: explain why we don't like "exclude rules" in refexes --- conf/example.conf | 2 +- doc/3-faq-tips-etc.mkd | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/conf/example.conf b/conf/example.conf index 14c6817..7170140 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -94,6 +94,6 @@ repo @privrepos thirdsecretrepo RW+ pu = bruce RW master next = bruce RW refs/tags/v[0-9].* = bruce - RW refs/tags/ = @secret_staff + RW refs/tags/ss/ = @secret_staff RW tmp/.* = @secret_staff R = @secret_staff diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 465959c..6fcc649 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -8,10 +8,13 @@ In this document: * differences from gitosis * two levels of access rights checking * error checking the config file + * built-in logging * one user, many keys * who am I? * other cool things * developer specific branches + * design choices + * why we don't do "excludes" ### common errors and mistakes @@ -249,3 +252,52 @@ first check: RW dummy = user3 Just don't *show* the user this config file; it might sound insulting :-) + +### design choices + +#### why we don't do "excludes" + +I found an error in the example conf file. This snippet *seems* to say that +"bruce" can write versioned tags (`refs/tags/v[0-9].*`), but the other +staffers can't: + + @staff = bruce whitfield martin + [... and later ...] + RW refs/tags/v[0-9].* = bruce + RW refs/tags = @staff + +But that's not how the matching works. As long as any refex matches the +refname being updated, it's a "yes". So the second refex lets anyone on +`@staff` create versioned tags, not just Bruce. + +One way to fix this is to allow "excludes" -- some changes in syntax, combined +with a rigorous, ordered, interpretation would do it. + +But if you're ever played with squid ACLs, or the include/exclude rules for +rsync, or rdiff-backup, or even git's own ignore mechanism, you'll see why I +won't do this. It bloats the code and the docs, and, despite all the docs, +*still* confuses people, which may then *reduce* security! + +Squid, rsync, gitignore, and all *need* the feature and so tolerate all this; +but we don't need it. All we need to do is make the refexes *disjoint* in +what they match (i.e., ensure that no refname can be matched by more than one +refex): + + RW refs/tags/v[0-9].* = bruce + RW refs/tags/staff/ = @staff + +In general, you probably want to control the refnames writable by devs anyway, +if at least to maintain some sanity, so being forced to make the refexes +disjoint is not a big problem. Here's an example: only the `project_lead` can +make arbitrarily named refs, while the rest have to stay within their assigned +namespaces: + + RW+ = project_lead + RW refs/tags/qa/ = @qa_team + RW bugID/ = @dev_team + RW trac/ = @dev_team + +The lack of overlap between refexes ensures ***no confusion*** in specifying, +understanding, and ***auditing***, what is allowed and what is not. + +And in security, "no confusion" is a good thing :-) From 09fd745255d5c2d7e37f9d888065d0de05ee484c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Sep 2009 09:10:35 +0530 Subject: [PATCH 071/850] upgrade doc: added step to compile --- doc/0-UPGRADE.mkd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/0-UPGRADE.mkd b/doc/0-UPGRADE.mkd index 9b0d9a5..9086661 100644 --- a/doc/0-UPGRADE.mkd +++ b/doc/0-UPGRADE.mkd @@ -30,6 +30,11 @@ have to do it at a "quiet" time or something. src/install.pl +5. compile the config once again, in case the *internal* format of the + compiled config file (`$GL_CONF_COMPILED`) has changed + + src/gl-compile-conf + And you're done. ### upgrade notes for specific versions From 2285e75c222df1768490c4a2c58d4c4226528ee5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Sep 2009 17:04:43 +0530 Subject: [PATCH 072/850] example rc: say that $PERSONAL must start with "refs/" --- conf/example.gitolite.rc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index d2adbfd..ee5a3e3 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -67,10 +67,13 @@ $GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm"; $PERSONAL=""; -# uncomment one of these if you do want it. I recommend this: +# uncomment one of these if you do want it. If you change it, remember it +# MUST start with "refs/" + +# I recommend this: # $PERSONAL="refs/personal"; -# but if you want something more visible/noisy, use this: +# if you want something more visible/noisy, use this: # $PERSONAL="refs/heads/personal"; # NOTE: whatever value you choose, for security reasons it is better to make From 978046acb9d7adb9d352eb275f20f958cbe46551 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Sep 2009 18:00:14 +0530 Subject: [PATCH 073/850] compile/update hook: COMPILED FILE CHANGE -- PLEASE READ BELOW Summary: DONT forget to run src/gl-compile-conf as the last step in the upgrade Details: The compiled file format has changed quite a bit, to make it easier for the rebel edition coming up :-) compile: - we don't split RW/RW+ into individual perms anymore - we store the info required for the first level check separately now: (repo, R/W, user) - the order for second level check is now: repo, user, [{ref=>perms}...] (list of hashes) update hook logic: the first refex that: - matches the incoming ref, AND - contains the perm you're trying to use, causes the match loop to exit with success. Fallthrough is failure --- doc/0-UPGRADE.mkd | 6 ++++++ src/gl-compile-conf | 15 ++++++++++----- src/update-hook.pl | 20 ++++++++++++-------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/doc/0-UPGRADE.mkd b/doc/0-UPGRADE.mkd index 9086661..a374c05 100644 --- a/doc/0-UPGRADE.mkd +++ b/doc/0-UPGRADE.mkd @@ -42,6 +42,12 @@ And you're done. If any extra steps beyond the generic ones above are needed, they will be listed here, newest first. +#### upgrading from 86faae4 + +Between 86faae4 and this version, gitolite had a *major* change in the +*internal* format of the compiled config file. Please do not omit step 5 in +the generic instructions above. + #### upgrading from 5758f69 Between 5758f69 and this version, gitolite learnt to allow "groupnames" for diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 3154dc1..33cc057 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -3,6 +3,7 @@ use strict; use warnings; use Data::Dumper; +$Data::Dumper::Indent = 1; # === add-auth-keys === @@ -146,8 +147,7 @@ while (<$conf_fh>) # actual permission line elsif (/^(R|RW|RW\+) (.* )?= (.+)/) { - # split perms to separate out R, W, and + - my @perms = split //, $1; + my $perms = $1; my @refs; @refs = split(' ', $2) if $2; my @users = split ' ', $3; @@ -165,11 +165,16 @@ while (<$conf_fh>) # ok, we can finally populate the %repos hash for my $repo (@repos) # each repo in the current stanza { - for my $perm (@perms) + for my $user (@users) { - for my $user (@users) + # for 1st level check (see faq/tips doc) + $repos{$repo}{R}{$user} = 1 if $perms =~ /R/; + $repos{$repo}{W}{$user} = 1 if $perms =~ /W/; + + # for 2nd level check, store each "ref, perms" pair in order + for my $ref (@refs) { - push @{ $repos{$repo}{$perm}{$user} }, @refs; + push @{ $repos{$repo}{$user} }, { $ref => $perms }; } } } diff --git a/src/update-hook.pl b/src/update-hook.pl index c703723..f65641e 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -58,13 +58,17 @@ $perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); $perm = '+' if $oldsha ne $merge_base; my @allowed_refs; -push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{$ENV{GL_USER}} || [] }; -push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$perm}{'@all'} || [] }; -push @allowed_refs, "$PERSONAL/$ENV{GL_USER}/" if $PERSONAL; -for my $refex (@allowed_refs) -# refex? sure -- a regex to match a ref against :) +# personal stuff -- right at the start in the new regime, I guess! +push @allowed_refs, { "$PERSONAL/$ENV{GL_USER}/" => "RW+" } if $PERSONAL; +# we want specific perms to override @all, so they come first +push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$ENV{GL_USER}} || [] }; +push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; +for my $ar (@allowed_refs) { - if ($ref =~ /$refex/) + my $refex = (keys %$ar)[0]; + # refex? sure -- a regex to match a ref against :) + next unless $ref =~ /$refex/; + if ($ar->{$refex} =~ /\Q$perm/) { # if log failure isn't important enough to block pushes, get rid of # all the error checking @@ -72,9 +76,9 @@ for my $refex (@allowed_refs) or die "open log failed: $!\n"; print $log_fh "$ENV{GL_TS} $perm\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . - "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\n"; + "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\t$refex\n"; close $log_fh or die "close log failed: $!\n"; exit 0; } } -exit 1; +die "$perm $ref $ENV{GL_USER} DENIED by fallthru\n"; From 2a763dfdb11f09ed05fda569e64566f69e7db8d4 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 20 Sep 2009 06:37:15 +0530 Subject: [PATCH 074/850] doc/3: updated the log line description --- doc/3-faq-tips-etc.mkd | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 6fcc649..d919087 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -69,9 +69,6 @@ normal way, since it's not empty anymore. to hurt anything. This happens even in normal git, not just gitolite. [Update 2009-09-14; this has been fixed in git 1.6.4.3] - * once in a while, if you're feeling particularly BOFH-ish, take a look at - `$GL_ADMINDIR/log` :-) - * if you specify a repo that is not at the top level `$REPO_BASE`, be sure to manually create the intermediate directories first. For instance if you specify a new repo called "a/b/c" to the config file and "compile", @@ -154,13 +151,17 @@ not unless you add a one-line patch to gitosis, plus a `post-receive` hook to every repository. With gitolite, there's a log file in `$GL_ADMINDIR` that contains lines like -this [I have abbreviated the SHAs for brevity in this document; the actual log -file will have all 40 characters]: +this: - +: username reponame refs/heads/branchname d0188d1 c5c00b6 + 2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master The "+" at the start indicates a non-fast forward update, in this case from -d0188d1 to c5c00b6 So d0188d1 is the one to restore! Can it get easier? +b4e76569659939 to 4fb16f2a88d8b5. So b4e76569659939 is the one to restore! +Can it get easier? + +The other parts of the log line are the name of the repo, the refname being +updated, the user updating it, and the refex pattern (from the config file) +that matched, in case you need to debug the config file itself. #### one user, many keys From 8217ef9d5bb0d14e96f16e00fdd16d8608e44b5c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 20 Sep 2009 21:25:16 +0530 Subject: [PATCH 075/850] P-T-A doc: add note about switching back and forth --- doc/4-push-to-admin.mkd | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/4-push-to-admin.mkd b/doc/4-push-to-admin.mkd index 12a6bc3..3fc83bf 100644 --- a/doc/4-push-to-admin.mkd +++ b/doc/4-push-to-admin.mkd @@ -12,17 +12,24 @@ people new to git, or ssh or (worse) both. However, ***if*** you know git and ssh really, *really*, well and you know what you're doing, this is a pretty nice thing to have -- does make life -easier, I admit. +easier, I admit. So, here is how to make PTA (hey nice acronym, just missing +an "I") work on gitolite as well. -So, here is how to make PTA (hey nice acronym, just missing an "I") work on -gitolite as well. But remember, there is NO SUPPORT! Go away. Leave me -alone... Anything else on gitolite I will help but not this, ok? :-) +Note: + + * unlike the rest of gitolite, I can't help you with this unless you + convince me very quickly it's not a layer 8 problem :-) + + * here's a test to see if you should use this feature: after reading this + document, think about how you would switch back and forth between the + normal method and push-to-admin. If you can't immediately see what you + would need to do, please don't use it :-) The instructions are presented as shell commands; they should be fairly obvious. All paths are from the default `~/.gitolite.rc`; if you changed any, make the same changes below. -> ---- +---- > **WARNING**: the "compilation" runs via a `post-update` hook. Which, by > definition, runs *after* the push has successfully completed. As a From 70d26d810b4089f2f66577fc68adc4b1c6e53f0e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 25 Sep 2009 12:17:33 +0530 Subject: [PATCH 076/850] compile, all docs/confs: specify gitweb/daemon access + bonus bonus: documented the "bits and pieces" thing properly; should have done this long ago, but it came to the forefront now thanks to this item --- README.mkd | 6 +++- conf/example.conf | 21 +++++++++-- conf/example.gitolite.rc | 5 +++ doc/0-UPGRADE.mkd | 6 ++++ doc/1-migrate.mkd | 2 ++ doc/2-admin.mkd | 23 ++++++++++++ doc/3-faq-tips-etc.mkd | 77 ++++++++++++++++++++++++++++++++++++++++ src/gl-compile-conf | 74 +++++++++++++++++++++++++++++++++++++- 8 files changed, 209 insertions(+), 5 deletions(-) diff --git a/README.mkd b/README.mkd index fa8e2f4..5a59bd1 100644 --- a/README.mkd +++ b/README.mkd @@ -38,7 +38,11 @@ that: * no one in $DAYJOB type environments will use or approve access methods that work without any authentication, so I didn't need gitweb/daemon - support in the tool or in the config file + support in the tool or in the config file. + + Update 2009-09-24: I don't use this feature but someone wanted it, so I + added it... see the "faq, tips, etc" document for more + * the idea that you admin it by pushing to a special repo is nice, but not really necessary because of how rarely these changes are made, especially considering how much code is involved in that piece diff --git a/conf/example.conf b/conf/example.conf index 7170140..6ea6234 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -8,9 +8,13 @@ # objectives, over and above gitosis: # - simpler syntax -# - no gitweb/daemon control -# - allows ff/non-ff control -# - allows branch level control +# - easier gitweb/daemon control +# - specify who can push a branch/tag +# - specify who can rewind a branch/rewrite a tag + +# convenience: allow specifying the access control in bits and pieces, even if +# they overlap. Keeps the config file smaller and saner. See the example in +# the "faq, tips, etc" document # ---------------------------------------------------------------------------- # LISTS @@ -97,3 +101,14 @@ repo @privrepos thirdsecretrepo RW refs/tags/ss/ = @secret_staff RW tmp/.* = @secret_staff R = @secret_staff + +# ---------------------------------------------------------------------------- +# GITWEB AND DAEMON CONTROL + +# there is no special syntax for this. If a repo gives read permissions to +# the special user "gitweb" or "daemon", the corresponding changes are made +# when you compile; see "faq, tips, etc" document for details. + +# this means you cannot have a real user called "gitweb" or "daemon" but I +# don't think that is a problem :-) + diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index ee5a3e3..3725110 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -24,6 +24,11 @@ $REPO_UMASK = 0077; # gets you 'rwx------' # $REPO_UMASK = 0027; # gets you 'rwxr-x---' # $REPO_UMASK = 0022; # gets you 'rwxr-xr-x' +# part of the setup of gitweb is a variable called $projects_list (please see +# gitweb documentation for more on this). Set this to the same value: + +$PROJECTS_LIST = "/home/git/projects.list"; + # -------------------------------------- # I see no reason anyone may want to change the gitolite admin directory, but diff --git a/doc/0-UPGRADE.mkd b/doc/0-UPGRADE.mkd index a374c05..8c80bb7 100644 --- a/doc/0-UPGRADE.mkd +++ b/doc/0-UPGRADE.mkd @@ -42,6 +42,12 @@ And you're done. If any extra steps beyond the generic ones above are needed, they will be listed here, newest first. +#### upgrading from 8217ef9 + +Between 8217ef9 and this version, gitolite learnt to handle gitweb/daemon +access. As a result, the rc file acquired a new variable, `$PROJECTS_LIST`, +which you have to set to whatever your gitweb installation requires. + #### upgrading from 86faae4 Between 86faae4 and this version, gitolite had a *major* change in the diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd index 6ef0c90..fe0b4cf 100644 --- a/doc/1-migrate.mkd +++ b/doc/1-migrate.mkd @@ -1,5 +1,7 @@ # migrating from gitosis to gitolite +[TODO: make the migration tool fix up gitweb and daemon control also...] + Migrating from gitosis to gitolite is pretty easy, because the basic design is the same. The differences are: diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 0585e06..8cd2216 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -32,6 +32,29 @@ Please read on to see how to do this correctly. should be exactly the same as their keyfile names, but without the `.pub` extension +#### specifying gitweb and daemon access + +This is a feature that I personally do not use (corporate environments don't +like unauthenticated access of any kind to any repo!), but someone wanted it, +so here goes. + +There's **no special syntax** for this -- just give read permission to a user +called `gitweb` or `daemon`! (This also means you can't have a normal user +with either of those two names, but I doubt that's a problem!). See the "faq, +tips, etc" document for easy ways to specify access for multiple repositories. + +Note that this does **not** install or configure gitweb/daemon -- that is a +one-time setup you must do separately. All this does is: + + * for gitweb, add the repo to the list of projects to be served by gitweb + (see the config file variable `$PROJECTS_LIST`, which should have the same + value you specified for `$projects_list` when setting up gitweb) + * for daemon, create the file `git-daemon-export-ok` in the repository + +`src/gl-compile-conf` will keep these files consistent with the config +settings -- this includes removing such settings if you remove "read" +permissions for the special usernames. + #### compiling * backup your `~/.ssh/authorized_keys` file if you feel nervous :-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index d919087..b28c548 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -6,8 +6,10 @@ In this document: * git version dependency * other errors, warnings, notes... * differences from gitosis + * simpler syntax * two levels of access rights checking * error checking the config file + * easier to specify gitweb/daemon access * built-in logging * one user, many keys * who am I? @@ -92,6 +94,49 @@ Apart from the big ones listed in the top level README, and subjective ones like "better config file format", there are some small, but significant and concrete, differences from gitosis. +#### simpler syntax + +The basic syntax is simpler and cleaner but it goes beyond that: **you can +specify access in bits and pieces**, even if they overlap. + +Some access needs are best grouped by repo, some by username, and some by +both. So just do all of them, and gitolite will combine all the access lists! +Here's an example: + + # define groups of people + @bosses = phb1 phb2 phb3 + @devs = dev1 dev2 dev3 + @interns = int1 int2 int3 + + # define groups of projects + @open = git gitolite linux rakudo + @closed = c1 c2 c3 + @topsecret = ts1 ts2 ts3 + + # all bosses have read access to all projects + repo @open @closed @topsecret + R = @bosses + + # everyone has read access to "open" projects + repo @open + R = @bosses @devs @interns + + [...or any other combination you want...] + + # later in the file: + + # specify access for individual repos (like RW, RW+, etc) + repo c1 + [...] + + [...etc...] + +If you notice that `@bosses` are given read access to `@open` via both rules, +do not worry that this causes some duplication or inefficiency. It doesn't +:-) + +See the "specify gitweb/daemon access" section below for one more example. + #### two levels of access rights checking Gitolite has two levels of access checks. The **first check** is what I will @@ -134,6 +179,38 @@ In gitolite, you have to "compile" the config file first (this step takes the place of the commit+push in gitosis), and keyword typos *are* caught so you know right away. +#### easier to specify gitweb/daemon access + +Specifying gitweb and/or daemon access for a repo is simple: give "read" +permissions to two special usernames: `gitweb` and `daemon`. + +You can also keep these pieces separate from the detailed, branch level access +for each repo, if you like, since you can write the access control specs in +bits and pieces. Here's an example, using short repo names for convenience: + + # maybe near the top of the file, for ease of access: + + @only_web = r1 r2 r3 + @only_daemon = r4 r5 r6 + @web_and_daemon = r7 r8 r9 + + repo @only_web + R = gitweb + repo @only_daemon + R = daemon + repo @web_and_daemon + R = gitweb + R = daemon + + # ...maybe much later in the file: + + repo r1 + # normal developer access lists for r1 and its branches/tags in the + # usual way + + repo r2 + # ...and so on... + #### built-in logging ...just in case of emergency :-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 33cc057..2422bc2 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -15,6 +15,11 @@ $Data::Dumper::Indent = 1; # (gl-)update hook need this, and it seems easier to do this than # replicate the parsing code in both those places. As a bonus, it's # probably more efficient. +# (3) - finally does what I have resisted doing all along -- handle gitweb and +# git-daemon access. It won't *setup* gitweb/daemon for you -- you have +# to that yourself. What this does is make sure that "repo.git" +# contains the file "git-daemon-export-ok" (for daemon case) and the +# line "repo.git" exists in the "projects.list" file (for gitweb case). # how run: manual, by GL admin # when: @@ -42,7 +47,7 @@ $Data::Dumper::Indent = 1; # common definitions # ---------------------------------------------------------------------------- -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST); # now that this thing *may* be run via "push to admin", any errors have to # grab the admin's ATTENTION so he won't miss them among the other messages a @@ -227,6 +232,73 @@ warn "\n\t\t***** WARNING *****\n" . "\t\"git version dependency\" in doc/3-faq-tips-etc.mkd\n" if $git_too_old; +# ---------------------------------------------------------------------------- +# handle gitweb and daemon +# ---------------------------------------------------------------------------- + +# How you specify gitweb and daemon access is quite different from gitosis. I +# just assume you'll never have any *real* users called "gitweb" or "daemon" +# :-) These are now "pseduo users" -- giving them "R" access to a repo is all +# you have to do + +wrap_chdir("$repo_base_abs"); + +# get the current project list; note that the file may not yet exist if no +# gitweb access has been specified so far +my %projlist = (); +if (-f $PROJECTS_LIST) { + my $projlist_fh = wrap_open( "<", $PROJECTS_LIST); + while(<$projlist_fh>) { + chomp; + $projlist{$_} = 1; + } + close $projlist_fh; +} +my $projlist_changed = 0; + +# daemons first... +for my $repo (sort keys %repos) { + my $export_ok = "$repo.git/git-daemon-export-ok"; + if ($repos{$repo}{'R'}{'daemon'}) { + unless (-f $export_ok) { + system("touch $export_ok"); + print STDERR "daemon add $repo.git\n"; + } + } else { + if (-f $export_ok) { + unlink($export_ok); + print STDERR "daemon del $repo.git\n"; + } + } +} + +# ...then gitwebs +for my $repo (sort keys %repos) { + if ($repos{$repo}{'R'}{'gitweb'}) { + unless ($projlist{"$repo.git"}) { + # not in the old list; add it to the new one + $projlist{"$repo.git"} = 1; + $projlist_changed = 1; + print STDERR "gitweb add $repo.git\n"; + } + } else { + if ($projlist{"$repo.git"}) { + # delete it from new list + delete $projlist{"$repo.git"}; + $projlist_changed = 1; + print STDERR "gitweb del $repo.git\n"; + } + } +} + +# has there been a change? +if ($projlist_changed) { + print STDERR "updating gitweb project list $PROJECTS_LIST\n"; + my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); + print $projlist_fh join("\n", sort keys %projlist), "\n" if %projlist; + close $projlist_fh; +} + # ---------------------------------------------------------------------------- # "compile" ssh authorized_keys # ---------------------------------------------------------------------------- From c66e1ad73286e1a193373234142daab44e5f7dd6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 27 Sep 2009 08:02:36 +0530 Subject: [PATCH 077/850] compile: pubkey related linting added - warn about files in keydir/ that dont end with ".pub" - warn about pubkey files for which the user is not mentioned in config - warn more sternly about the opposite (user in config, no pubkey!) update hook: add reponame to message on deny auth: minor typo --- src/gl-auth-command | 2 +- src/gl-compile-conf | 26 +++++++++++++++++++++++--- src/update-hook.pl | 2 +- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index c301415..3822760 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -51,7 +51,7 @@ my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! # ---------------------------------------------------------------------------- # SSH_ORIGINAL_COMMAND must exist. Since we also captured $user, we print -# that in the message so people saying "ssh git@server" can see which gitosis +# that in the message so people saying "ssh git@server" can see which gitolite # user he is being recognised as my $cmd = $ENV{SSH_ORIGINAL_COMMAND} or die "no SSH_ORIGINAL_COMMAND? I'm not a shell, $user!\n"; diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 2422bc2..ae1ea24 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -71,6 +71,7 @@ my $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple patter # groups can now represent user groups or repo groups my %groups = (); my %repos = (); +my %user_list = (); # only to catch lint; search for "lint" below # set the umask before creating any files umask($REPO_UMASK); @@ -172,6 +173,8 @@ while (<$conf_fh>) { for my $user (@users) { + $user_list{$user}++; # only to catch lint, see later + # for 1st level check (see faq/tips doc) $repos{$repo}{R}{$user} = 1 if $perms =~ /R/; $repos{$repo}{W}{$user} = 1 if $perms =~ /W/; @@ -195,7 +198,7 @@ print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]); close $compiled_fh or die "$ATTN close compiled-conf failed: $!\n"; # ---------------------------------------------------------------------------- -# any new repos created? +# any new repos to be created? # ---------------------------------------------------------------------------- # modern gits allow cloning from an empty repo, so we just create it. Gitosis @@ -291,7 +294,7 @@ for my $repo (sort keys %repos) { } } -# has there been a change? +# has there been a change in the gitweb projects list? if ($projlist_changed) { print STDERR "updating gitweb project list $PROJECTS_LIST\n"; my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); @@ -317,12 +320,29 @@ while (<$authkeys_fh>) # options, in the standard ssh authorized_keys format), then the "end" line. print $newkeys_fh "# gitolite start\n"; wrap_chdir($GL_KEYDIR); -for my $pubkey (glob("*.pub")) +for my $pubkey (glob("*")) { + # lint check 1 + unless ($pubkey =~ /\.pub$/) + { + print STDERR "WARNING: pubkey files should end with \".pub\", ignoring $pubkey\n"; + next; + } my $user = $pubkey; $user =~ s/(\@.+)?\.pub$//; + # lint check 2 + print STDERR "WARNING: pubkey $pubkey exists but user $user not in config\n" + unless $user_list{$user}; + $user_list{$user} = 'has pubkey'; print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; print $newkeys_fh `cat $pubkey`; } +# lint check 3; a little more severe than the first two I guess... +for my $user (sort keys %user_list) +{ + next if $user eq '@all' or $user_list{$user} eq 'has pubkey'; + print STDERR "$ATTN user $user in config, but has no pubkey!\n"; +} + print $newkeys_fh "# gitolite end\n"; close $newkeys_fh or die "$ATTN close newkeys failed: $!\n"; diff --git a/src/update-hook.pl b/src/update-hook.pl index f65641e..5f1c3bb 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -81,4 +81,4 @@ for my $ar (@allowed_refs) exit 0; } } -die "$perm $ref $ENV{GL_USER} DENIED by fallthru\n"; +die "$perm $ref $ENV{GL_REPO} $ENV{GL_USER} DENIED by fallthru\n"; From 1b9969f3d6adb14559f939e082e170ee7f6f4fdc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 27 Sep 2009 23:52:04 +0530 Subject: [PATCH 078/850] auth: better message, remove unsightly perl warning on bad command --- conf/example.conf | 2 ++ src/gl-auth-command | 12 ++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 6ea6234..5e92b0a 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -5,6 +5,8 @@ # - comments in the normal shell-ish style; no surprises there # - there are no continuation lines of any kind # - user/repo names as simple as possible +# (usernames: only alphanumerics, ".", "_", "-"; +# reponames: same, plus "/", but not at the start) # objectives, over and above gitosis: # - simpler syntax diff --git a/src/gl-auth-command b/src/gl-auth-command index 3822760..62573d4 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -56,11 +56,6 @@ my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! my $cmd = $ENV{SSH_ORIGINAL_COMMAND} or die "no SSH_ORIGINAL_COMMAND? I'm not a shell, $user!\n"; -# this check is largely for comic value if someone tries something outrageous; -# $cmd gets split and the pieces examined more thoroughly later anyway -die "$cmd??? you're a funny guy...\n" - if $cmd =~ /[<>&|;\n]/; - # split into command and arguments; the pattern allows old style as well as # new style: "git-subcommand arg" or "git subcommand arg", just like gitosis # does, although I'm not sure how necessary that is @@ -70,9 +65,10 @@ die "$cmd??? you're a funny guy...\n" # including the single quotes my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*).git'/); -die "Sorry, I don't like the command you gave me: $cmd\n" - unless ( ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) - and $repo =~ $REPONAME_PATT ); +die "bad command: $cmd. Make sure the repo name is exactly\n" . + "as in your config (no extra stuff before the name), plus a \".git\" at the end\n" + unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) + and $repo and $repo =~ $REPONAME_PATT ); # ---------------------------------------------------------------------------- # first level permissions check From c15c75749bb0b2b4ae8752dd7cc4559aa687b2af Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 3 Oct 2009 10:55:30 +0530 Subject: [PATCH 079/850] compile: special-case 'gitweb' and 'daemon' from the linting not a big deal since there's a very simple and obvious workaround -- create a new keypair, throw away the private key, and use the pubkey --- src/gl-compile-conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index ae1ea24..9b13dd7 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -339,7 +339,7 @@ for my $pubkey (glob("*")) # lint check 3; a little more severe than the first two I guess... for my $user (sort keys %user_list) { - next if $user eq '@all' or $user_list{$user} eq 'has pubkey'; + next if $user =~ /^(gitweb|daemon|\@all)$/ or $user_list{$user} eq 'has pubkey'; print STDERR "$ATTN user $user in config, but has no pubkey!\n"; } From 3267c3f4be80d707e49287c4181f14d17f0e047d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 2 Oct 2009 21:54:23 +0530 Subject: [PATCH 080/850] compile: change %groups from hash of lists to hash of hashes This makes it easier to test if a repo is a member of a group, which is required for the delegation feature coming up --- src/gl-compile-conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 9b13dd7..e257c69 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -104,7 +104,7 @@ sub expand_list { die "$ATTN undefined group $item\n" unless $groups{$item}; # add those names to the list - push @new_list, @{ $groups{$item} }; + push @new_list, sort keys %{ $groups{$item} }; } else { @@ -139,7 +139,7 @@ while (<$conf_fh>) # user or repo groups if (/^(@\S+) = (.*)/) { - push @{ $groups{$1} }, expand_list( split(' ', $2) ); + do { $groups{$1}{$_} = 1 } for ( expand_list( split(' ', $2) ) ); # again, we take the more "relaxed" pattern die "$ATTN bad group $1\n" unless $1 =~ $REPONAME_PATT; } From 34a6f89c265fce7c3ce35ac130d85e7861209157 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 2 Oct 2009 22:17:51 +0530 Subject: [PATCH 081/850] compile: make the parse a function instead of inline Again, prep for delegation, when we'll be reading fragments of config rules from various files and tacking them onto the %repos hash. note: this patch best viewed with "git diff -w", clicking "Ignore space change" in gitk, or eqvt :-) --- src/gl-compile-conf | 128 +++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 61 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index e257c69..281eafd 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -119,80 +119,86 @@ sub expand_list # "compile" GL conf # ---------------------------------------------------------------------------- -my $conf_fh = wrap_open( "<", $GL_CONF ); - -# the syntax is fairly simple, so we parse it inline - -my @repos; -while (<$conf_fh>) +sub parse_conf_file { - # normalise whitespace; keeps later regexes very simple - s/=/ = /; - s/\s+/ /g; - s/^ //; - s/ $//; - # kill comments - s/#.*//; - # and blank lines - next unless /\S/; + my ($conffile) = @_; + my $conf_fh = wrap_open( "<", $conffile ); - # user or repo groups - if (/^(@\S+) = (.*)/) + # the syntax is fairly simple, so we parse it inline + + my @repos; + while (<$conf_fh>) { - do { $groups{$1}{$_} = 1 } for ( expand_list( split(' ', $2) ) ); - # again, we take the more "relaxed" pattern - die "$ATTN bad group $1\n" unless $1 =~ $REPONAME_PATT; - } - # repo(s) - elsif (/^repo (.*)/) - { - # grab the list and expand any @stuff in it - @repos = split ' ', $1; - @repos = expand_list ( @repos ); - } - # actual permission line - elsif (/^(R|RW|RW\+) (.* )?= (.+)/) - { - my $perms = $1; - my @refs; @refs = split(' ', $2) if $2; - my @users = split ' ', $3; + # normalise whitespace; keeps later regexes very simple + s/=/ = /; + s/\s+/ /g; + s/^ //; + s/ $//; + # kill comments + s/#.*//; + # and blank lines + next unless /\S/; - # if no ref is given, this PERM applies to all refs - @refs = qw(refs/.*) unless @refs; - # fully qualify refs that dont start with "refs/"; prefix them with - # "refs/heads/" - @refs = map { m(^refs/) or s(^)(refs/heads/); $_ } @refs; - - # expand the user list, unless it is just "@all" - @users = expand_list ( @users ) - unless (@users == 1 and $users[0] eq '@all'); - do { die "$ATTN bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; - - # ok, we can finally populate the %repos hash - for my $repo (@repos) # each repo in the current stanza + # user or repo groups + if (/^(@\S+) = (.*)/) { - for my $user (@users) + do { $groups{$1}{$_} = 1 } for ( expand_list( split(' ', $2) ) ); + # again, we take the more "relaxed" pattern + die "$ATTN bad group $1\n" unless $1 =~ $REPONAME_PATT; + } + # repo(s) + elsif (/^repo (.*)/) + { + # grab the list and expand any @stuff in it + @repos = split ' ', $1; + @repos = expand_list ( @repos ); + } + # actual permission line + elsif (/^(R|RW|RW\+) (.* )?= (.+)/) + { + my $perms = $1; + my @refs; @refs = split(' ', $2) if $2; + my @users = split ' ', $3; + + # if no ref is given, this PERM applies to all refs + @refs = qw(refs/.*) unless @refs; + # fully qualify refs that dont start with "refs/"; prefix them with + # "refs/heads/" + @refs = map { m(^refs/) or s(^)(refs/heads/); $_ } @refs; + + # expand the user list, unless it is just "@all" + @users = expand_list ( @users ) + unless (@users == 1 and $users[0] eq '@all'); + do { die "$ATTN bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; + + # ok, we can finally populate the %repos hash + for my $repo (@repos) # each repo in the current stanza { - $user_list{$user}++; # only to catch lint, see later - - # for 1st level check (see faq/tips doc) - $repos{$repo}{R}{$user} = 1 if $perms =~ /R/; - $repos{$repo}{W}{$user} = 1 if $perms =~ /W/; - - # for 2nd level check, store each "ref, perms" pair in order - for my $ref (@refs) + for my $user (@users) { - push @{ $repos{$repo}{$user} }, { $ref => $perms }; + $user_list{$user}++; # only to catch lint, see later + + # for 1st level check (see faq/tips doc) + $repos{$repo}{R}{$user} = 1 if $perms =~ /R/; + $repos{$repo}{W}{$user} = 1 if $perms =~ /W/; + + # for 2nd level check, store each "ref, perms" pair in order + for my $ref (@refs) + { + push @{ $repos{$repo}{$user} }, { $ref => $perms }; + } } } } - } - else - { - die "$ATTN can't make head or tail of '$_'\n"; + else + { + die "$ATTN can't make head or tail of '$_'\n"; + } } } +parse_conf_file($GL_CONF); + my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]); close $compiled_fh or die "$ATTN close compiled-conf failed: $!\n"; From 5bb0850c5c628f279c8bd16fdd5583c89402093d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 2 Oct 2009 23:23:52 +0530 Subject: [PATCH 082/850] p-t-a: make the post-update hook a separate file... ...and just refer to it in the doc. This hook will acquire more code soon, when we do delegations :) --- doc/4-push-to-admin.mkd | 19 +++++++------------ src/pta-hook.sh | 6 ++++++ 2 files changed, 13 insertions(+), 12 deletions(-) create mode 100755 src/pta-hook.sh diff --git a/doc/4-push-to-admin.mkd b/doc/4-push-to-admin.mkd index 3fc83bf..4b208a9 100644 --- a/doc/4-push-to-admin.mkd +++ b/doc/4-push-to-admin.mkd @@ -70,20 +70,15 @@ repos they play havoc with my git commands, so this is how I do it) GIT_WORK_TREE=/home/git/.gitolite git add conf/gitolite.conf keydir GIT_WORK_TREE=/home/git/.gitolite git commit -am start -Now we have to setup the post-update hook for push-to-admin to work. The -hook should (1) make a forced checkout in the "live" config directory (which -is `~/.gitolite`), and (2) run the compile script. So we create a hook with -the appropriate code in it, and then make it executable +Now we have to setup the post-update hook for push-to-admin to work. The hook +should (1) make a forced checkout in the "live" config directory (which is +`~/.gitolite`), and (2) run the compile script. - cat < hooks/post-update - #!/bin/sh - - GIT_WORK_TREE=/home/git/.gitolite git checkout -f - - cd /home/git/.gitolite - src/gl-compile-conf - EOFPU +`src/pta-hook.sh` has the code you need; just copy it to the right place with +the right name and make sure it is executable: + # (assuming pwd is still ~/repositories/gitolite-admin.git) + cp ~/.gitolite/src/pta-hook.sh hooks/post-update chmod +x hooks/post-update ---- diff --git a/src/pta-hook.sh b/src/pta-hook.sh new file mode 100755 index 0000000..283cc9f --- /dev/null +++ b/src/pta-hook.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +GIT_WORK_TREE=/home/git/.gitolite git checkout -f + +cd /home/git/.gitolite +src/gl-compile-conf From 2f2af033f53954e919ca0b9fae94e521fbaf47ba Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 3 Oct 2009 13:17:02 +0530 Subject: [PATCH 083/850] pta-hook.sh: collect delegated config fragments collect the delegated config fragments from correspondingly named branches of the gitolite-admin repo, and put them all in conf/fragments/ also deprecate changes to conf and keydir locations from now on --- conf/example.gitolite.rc | 8 ++++++-- src/pta-hook.sh | 27 +++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 3725110..ea80bec 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -57,8 +57,12 @@ $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m.log"; # -------------------------------------- -# I see even less reason to change these, since they're all relative to the -# gitolite admin directory above, but hey it's *your* system... +# Please DO NOT change the following paths unless you really know what you're +# doing. It'll work for now but it's officially deprecated to have them +# elsewhere from now on, and may break some future features. + +# Anyway, the conf files and keydirs don't grow constantly, (like the logs and +# the repositories do), so I don't think this is a major problem for anyone. $GL_CONF="$GL_ADMINDIR/conf/gitolite.conf"; $GL_KEYDIR="$GL_ADMINDIR/keydir"; diff --git a/src/pta-hook.sh b/src/pta-hook.sh index 283cc9f..cbc0d2f 100755 --- a/src/pta-hook.sh +++ b/src/pta-hook.sh @@ -1,6 +1,29 @@ #!/bin/sh -GIT_WORK_TREE=/home/git/.gitolite git checkout -f +# get this from your .gitolite.conf; and don't forget this is shell, while +# that is perl :-) +export GL_ADMINDIR=/home/git/.gitolite -cd /home/git/.gitolite +# checkout the master branch to $GL_ADMINDIR +GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master + +# collect all the delegated fragments +mkdir -p $GL_ADMINDIR/conf/fragments +for br in $(git for-each-ref --format='%(refname:short)') +do + # skip master (duh!) + [[ $br == master ]] && continue + # all other branches *should* contain a file called .conf + # inside conf/fragments; if so copy it + if git show $br:conf/fragments/$br.conf > /dev/null 2>&1 + then + git show $br:conf/fragments/$br.conf > $GL_ADMINDIR/conf/fragments/$br.conf + echo "(extracted $br conf; `wc -l < $GL_ADMINDIR/conf/fragments/$br.conf` lines)" + else + echo " ***** ERROR *****" + echo " branch $br does not contain conf/fragments/$br.conf" + fi +done + +cd $GL_ADMINDIR src/gl-compile-conf From fa5567f22cba52ff8d98a0010c508f18bf073c4f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 4 Oct 2009 09:25:20 +0530 Subject: [PATCH 084/850] doc/5-delegation added, doc/4 (PTA) enhanced This is complete user documentation for delegation --- doc/3-faq-tips-etc.mkd | 8 +++ doc/4-push-to-admin.mkd | 77 +++++++++++++---------- doc/5-delegation.mkd | 135 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 34 deletions(-) create mode 100644 doc/5-delegation.mkd diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index b28c548..66ab956 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -9,6 +9,7 @@ In this document: * simpler syntax * two levels of access rights checking * error checking the config file + * delegating parts of the config file * easier to specify gitweb/daemon access * built-in logging * one user, many keys @@ -179,6 +180,13 @@ In gitolite, you have to "compile" the config file first (this step takes the place of the commit+push in gitosis), and keyword typos *are* caught so you know right away. +#### delegating parts of the config file + +You can now split up the config file and delegate the authority to specify +access control for their own pieces. See +[doc/5-delegation.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/5-delegation.mkd) +for details. + #### easier to specify gitweb/daemon access Specifying gitweb and/or daemon access for a repo is simple: give "read" diff --git a/doc/4-push-to-admin.mkd b/doc/4-push-to-admin.mkd index 4b208a9..48d1baa 100644 --- a/doc/4-push-to-admin.mkd +++ b/doc/4-push-to-admin.mkd @@ -41,51 +41,60 @@ make the same changes below. ---- -First, on the server, log on to the `git` userid, add a new repo called -`gitolite-admin` to the config file, give yourself `RW` or `RW+` rights to it, -and "compile": +#### server side setup - cd ~/.gitolite - vim conf/gitolite.conf # add gitolite-admin repo, etc - src/gl-compile-conf +1. First, on the server, log on to the `git` userid, add a new repo called + `gitolite-admin` to the config file, give yourself `RW` or `RW+` rights to it, + and "compile": -Now, if you look at the "compile" script, it has an *automatic* local commit -inside, just for safety, which kicks in every time you compile. This only -works if it finds a ".git" directory, and it was designed as an "automatic -backup/safety net" type of thing, in case I accidentally deleted the whole -config file or something. + cd ~/.gitolite + vim conf/gitolite.conf # add gitolite-admin repo, etc + src/gl-compile-conf -We need to disable this, because now we have a *better* repo, one that is -manually pushed, and presumably has proper commit messages! +2. Now, if you look at the "compile" script, it has an *automatic* local commit + inside, just for safety, which kicks in every time you compile. This only + works if it finds a ".git" directory, and it was designed as an "automatic + backup/safety net" type of thing, in case I accidentally deleted the whole + config file or something. - mv .git .disable.git # yeah it's a hack, sue me + We need to disable this, because now we have a *better* repo, one that is + manually pushed, and presumably has proper commit messages! -Now the compile command created an empty, bare, "gitolite-admin" repo, so we -seed it with the current contents of the config and keys. (A note on the -`GIT_WORK_TREE` variable: I avoid setting these variables in the normal way -because I always forget to unset them later, and then when I `cd` to other -repos they play havoc with my git commands, so this is how I do it) + mv .git .disable.git # yeah it's a hack, sue me - cd ~/repositories/gitolite-admin.git - GIT_WORK_TREE=/home/git/.gitolite git add conf/gitolite.conf keydir - GIT_WORK_TREE=/home/git/.gitolite git commit -am start +3. Now the compile command created an empty, bare, "gitolite-admin" repo, so we + seed it with the current contents of the config and keys. (A note on the + `GIT_WORK_TREE` variable: I avoid setting these variables in the normal way + because I always forget to unset them later, and then when I `cd` to other + repos they play havoc with my git commands, so this is how I do it) -Now we have to setup the post-update hook for push-to-admin to work. The hook -should (1) make a forced checkout in the "live" config directory (which is -`~/.gitolite`), and (2) run the compile script. + cd ~/repositories/gitolite-admin.git + GIT_WORK_TREE=/home/git/.gitolite git add conf/gitolite.conf keydir + GIT_WORK_TREE=/home/git/.gitolite git commit -am start -`src/pta-hook.sh` has the code you need; just copy it to the right place with -the right name and make sure it is executable: +4. Now we have to setup the post-update hook for push-to-admin to work. The hook + should (1) make a forced checkout in the "live" config directory (which is + `~/.gitolite`), and (2) run the compile script. - # (assuming pwd is still ~/repositories/gitolite-admin.git) - cp ~/.gitolite/src/pta-hook.sh hooks/post-update - chmod +x hooks/post-update + `src/pta-hook.sh` has the code you need; just copy it to the right place with + the right name, change the first line if needed, and make it executable: + + # (assuming pwd is still ~/repositories/gitolite-admin.git) + cp ~/.gitolite/src/pta-hook.sh hooks/post-update + + # if you changed $GL_ADMINDIR in ~/.gitolite.conf, then edit the hooks + # and change the first line: + vim hooks/post-update + + chmod +x hooks/post-update ---- -Now get to your workstation, and +#### client side setup - git clone git@server:gitolite-admin.git +1. Now get to your workstation, and -That's it, we're done. You're in gitosis land as far as this is concerned -now. So knock yourself out. Or lock yourself out... :-) + git clone git@server:gitolite-admin.git + + That's it, we're done. You're in gitosis land as far as this is concerned + now. So knock yourself out. Or lock yourself out... :-) diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd new file mode 100644 index 0000000..7592e83 --- /dev/null +++ b/doc/5-delegation.mkd @@ -0,0 +1,135 @@ +# delegating access control responsibilities + +[Thanks to jeromeag for forcing me to think through this...] + +### lots of repos, lots of users + +Gitolite tries to make it easy to manage access to lots of users and repos, +exploiting commonalities wherever possible. (The example under "simpler, more +powerful syntax" [here][ml] should give you an idea). As you can see, it lets +you specify bits and pieces of the access control separately -- i.e., *all* +the access specs for a certain repo need not be together; they can be +scattered, which makes it easier to manage the sort of slice and dice needed +in that example. + +[ml]: http://github.com/sitaramc/gitolite/blob/ml/update.mkd + +But eventually the config file will become too big. If you let only one +person have control, he could become a bottleneck. If you give it to multiple +people, they might make mistakes or stomp on each others' work accidentally. + +The best way is to divide up the config file and give parts of it to different +people. Ideally, we would delegate authority for *groups* of repos, not +individual repos, otherwise it doesn't scale. + +It would also be nice if we could specify what repos can be delegated to a +particular admin, and prevent him/her from specifying access control for any +other repos. This would be a nice "security" feature. + +### splitting up the config file into fragments + +To start with, recall that gitolite allows you to specify **groups** (of users +or repos, same syntax). So the basic idea is that the main config file +(`~/.gitolite/conf/gitolite.conf` by default) will specify some repo groups: + + # group your projects/repos however you want + @webbrowser_repos = firefox lynx + @webserver_repos = apache nginx + @malware_repos = conficker storm + + # any other config as usual, including access control lines for any of the + # above projects or groups + +Now just create these files (assuming default `$GL_ADMINDIR` location): + + ~/.gitolite/conf/fragments/webbrowser_repos.conf + ~/.gitolite/conf/fragments/webserver_repos.conf + ~/.gitolite/conf/fragments/malware_repos.conf + +Within each of those files put in whatever access control rules you want for +the repos that are members of that group. Notice that the basenames of the +files must be exactly the same as the name of the corresponding repo group in +the main config file. + +For instance, `~/.gitolite/conf/fragments/webbrowser_repos.conf` would only +contain access control for firefox and lynx. If it referenced any other repo +(say "storm") those lines would be ignored (and a warning message generated). + +When you run the compile script (`src/gl-compile-conf`), the **net effect is +as if you appended the contents of all the "fragment" files, in alphabetical +order, to the bottom of the main file**. + +(Except of course, while processing a fragment, it will ignore attempts to set +permissions for repos that are not members of the same-named "repo group"). + +And that's basically it, in the simplest usage. + +["But WAIT, there's MORE!"][bwtm] + +[bwtm]: http://en.wikipedia.org/wiki/Ed_Valenti#But_Wait.21_There.27s_More + +### delegating ownership of fragments + +Splitting up the file does help, but there's also that little security issue +-- anyone can make any change to any "fragment", unless you (once again) go +back to Unix permissions to keep them separate. + +Fixing that requires using "push-to-admin". + +The page on [push-to-admin][ptd] explains clearly how to set it up. Unlike +gitosis, I refuse to make it the default because it's a support nightmare. +Don't get me wrong -- it's a great feature, and I use it myself, but the +learning curve is too steep to make it *required*. + +[ptd]: http://github.com/sitaramc/gitolite/blob/pu/doc/4-push-to-admin.mkd + +So, having setup push-to-admin, you add these lines to the main config file, +assuming Alice is in charge of all web browser development projects, Bob takes +care of web servers, and Mallory, as [tradition][abe] dictates, is in charge +of malware ;-) + +[abe]: http://en.wikipedia.org/wiki/Alice_and_Bob#List_of_characters + + # you probably added these two lines while setting up push-to-admin + repo gitolite-admin + RW+ = sitaram + # now add these 3 lines + RW webbrowser_repos = alice + RW webserver_repos = bob + RW malware_repos = mallory + +As you can see, for each repo group you want to delegate authority over, +there's a **branch** in the `gitolite-admin` repo with the same name. Whoever +has write access to that branch, is allowed to define rules for repos in the +corresponding "repo group". + +In other words, **we use gitolite's per-branch permissions to "enforce" the +separation between the delegated configs!** + +Here's how to use this in practice: + + * Alice clones the `gitolite-admin` repo, creates (if not already created) and + checks out a new branch called `webbrowser_repos`, and adds a file called + `conf/fragments/webbrowser_repos.conf` in that branch + + * (the rest of the contents of that branch do not matter; she can keep + all the other files or delete all of them -- it doesn't make any + difference. Only that one specific file is used). + + * she writes in this file any access control rules for the "firefox" and + "lynx" repos. She should not write access rules for any other project -- + they will be ignored + + * Alice then commits and pushes this branch to the `gitolite-admin` repo + +Naturally, a successful push invokes the post-update hook that you installed +(while setting up [push-to-admin][ptd]). Here's what it does: + + * for each branch, say `br`, of the `gitolite-admin` repo, it checks if + there is a file called `conf/fragments/br.conf` + + * if there is, it extracts it and copies it with the exact same name and + path, into the `$GL_ADMINDIR` directory (`~/.gitolite` by default) + +After that, it runs the compile script, and things work the same as described +in the previous section. From 616d8a5f7d2c8c08c7cc1f52dc89bd5846695d19 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 4 Oct 2009 09:56:40 +0530 Subject: [PATCH 085/850] compile: (large changes) parse delegated fragments if any [Note: this is a fairly involved commit, compared to most of the others. See doc/5-delegation.mkd for a user-level feature description.] parse delegated config fragments (found as conf/fragments/*.conf). Any repos being referenced within a fragment config *must* belong to the "@group" with the same name as the fragment. That is, a fragment called conf/fragments/abc.conf can only refer to repos that are members of the "@abc" repo group. It cannot specify access control for any other repos. If it does, those settings are ignored, and a warning message is produced. since the delegated config must have the flexibility of (re-)defining group names for internal convenience, and since all such definitions go into the same "groups" hash, it is quite easy for conf/fragments/abc.conf to write in its own (re-)definition of "@abc"! That would be a neat little security hole :) The way to close it is to consider only members of the "@abc" groupset defined in the main ("master") config file for this purpose. --- src/gl-compile-conf | 47 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 281eafd..8f97bf0 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -121,7 +121,14 @@ sub expand_list sub parse_conf_file { - my ($conffile) = @_; + my ($conffile, $fragment) = @_; + # the second arg, $fragment, is passed in as "master" when parsing the + # main config, and the fragment name when parsing a fragment. In the + # latter case, the parser uses that information to ignore (and warn about) + # any repos in the fragment that are not members of the "repo group" of + # the same name. + my %ignored = (); + my $conf_fh = wrap_open( "<", $conffile ); # the syntax is fairly simple, so we parse it inline @@ -142,7 +149,9 @@ sub parse_conf_file # user or repo groups if (/^(@\S+) = (.*)/) { - do { $groups{$1}{$_} = 1 } for ( expand_list( split(' ', $2) ) ); + # store the members of each group as hash key. Keep track of when + # the group was *first* created by using $fragment as the *value* + do { $groups{$1}{$_} ||= $fragment } for ( expand_list( split(' ', $2) ) ); # again, we take the more "relaxed" pattern die "$ATTN bad group $1\n" unless $1 =~ $REPONAME_PATT; } @@ -174,6 +183,22 @@ sub parse_conf_file # ok, we can finally populate the %repos hash for my $repo (@repos) # each repo in the current stanza { + # if we're processing a delegated config file (not the master + # config), and if that fragment name is not the same as the + # current repo + if ($fragment ne 'master' and $fragment ne $repo) + { + # then the fragment must be a group name and the repo + # being processed must be a member of that "@group". + # Also, the value of the hash for that combination must be + # "master", signifying a group created in the master + # config file and not in one of the delegates + unless ( ($groups{"\@$fragment"}{$repo} || '') eq 'master') + { + $ignored{$fragment}{$repo} = 1; + next; + } + } for my $user (@users) { $user_list{$user}++; # only to catch lint, see later @@ -195,9 +220,25 @@ sub parse_conf_file die "$ATTN can't make head or tail of '$_'\n"; } } + for my $ig (sort keys %ignored) + { + warn "\n\t\t***** WARNING *****\n" . + "\t$ig.conf attempting to set access for " . + join (", ", sort keys %{ $ignored{$ig} }) . "\n"; + } } -parse_conf_file($GL_CONF); +# parse the main config file +parse_conf_file($GL_CONF, 'master'); + +# parse any delegated fragments +wrap_chdir($GL_ADMINDIR); +for my $fragment_file (glob("conf/fragments/*.conf")) +{ + my $fragment = $fragment_file; + $fragment =~ s/^conf\/fragments\/(.*).conf$/$1/; + parse_conf_file($fragment_file, $fragment); +} my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]); From 3c960aa5e1a6a692b811b4c76b5c414b810dcab3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 4 Oct 2009 15:51:32 +0530 Subject: [PATCH 086/850] pta hook: avoid spurious error messages on old fragments --- src/pta-hook.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pta-hook.sh b/src/pta-hook.sh index cbc0d2f..23a2f23 100755 --- a/src/pta-hook.sh +++ b/src/pta-hook.sh @@ -7,8 +7,12 @@ export GL_ADMINDIR=/home/git/.gitolite # checkout the master branch to $GL_ADMINDIR GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master +# remove all fragments. otherwise, you get spurious error messages when you +# take away someone's delegation in the main config but the fragment is still +# hanging around. The ones that are valid will get re-created anyway +rm -rf $GL_ADMINDIR/conf/fragments # collect all the delegated fragments -mkdir -p $GL_ADMINDIR/conf/fragments +mkdir $GL_ADMINDIR/conf/fragments for br in $(git for-each-ref --format='%(refname:short)') do # skip master (duh!) From 8096cc8e9c9ecdfb511c4e1c859def54878f44ee Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 5 Oct 2009 16:08:10 +0530 Subject: [PATCH 087/850] install.pl, pta hook, upgrade doc: - install the post-update hook also - fix bashism in pta-hook Also, since delegation works best with PTA, reflect that in the upgrade doc --- doc/0-UPGRADE.mkd | 3 +++ src/install.pl | 6 ++++++ src/pta-hook.sh | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/0-UPGRADE.mkd b/doc/0-UPGRADE.mkd index 8c80bb7..c106bc7 100644 --- a/doc/0-UPGRADE.mkd +++ b/doc/0-UPGRADE.mkd @@ -35,6 +35,9 @@ have to do it at a "quiet" time or something. src/gl-compile-conf + (if you've already setup "push-to-admin", this step should be replaced by + a "git push". Make a dummy commit if needed, to make the push happen). + And you're done. ### upgrade notes for specific versions diff --git a/src/install.pl b/src/install.pl index 85b8607..017854f 100755 --- a/src/install.pl +++ b/src/install.pl @@ -64,3 +64,9 @@ for my $repo (`find . -type d -name "*.git"`) { system("cp $GL_ADMINDIR/src/update-hook.pl $repo/hooks/update"); chmod 0755, "$repo/hooks/update"; } + +# oh and one of those repos is a bit more special and has an extra hook :) +system("cp $GL_ADMINDIR/src/pta-hook.sh gitolite-admin.git/hooks/post-update"); +system("perl", "-i", "-p", "-e", "s(export GL_ADMINDIR=.*)(export GL_ADMINDIR=$GL_ADMINDIR)", + "gitolite-admin.git/hooks/post-update"); +chmod 0755, "gitolite-admin.git/hooks/post-update"; diff --git a/src/pta-hook.sh b/src/pta-hook.sh index 23a2f23..5009313 100755 --- a/src/pta-hook.sh +++ b/src/pta-hook.sh @@ -16,7 +16,8 @@ mkdir $GL_ADMINDIR/conf/fragments for br in $(git for-each-ref --format='%(refname:short)') do # skip master (duh!) - [[ $br == master ]] && continue + [ "$br" = "master" ] && continue + # all other branches *should* contain a file called .conf # inside conf/fragments; if so copy it if git show $br:conf/fragments/$br.conf > /dev/null 2>&1 From ec2ad64b384aaeac6a57e8a06fa686eac1a1b5d5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 5 Oct 2009 17:52:33 +0530 Subject: [PATCH 088/850] doc/delegation: never ending quest to write well :) --- doc/5-delegation.mkd | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index 7592e83..9924dc3 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -19,15 +19,22 @@ person have control, he could become a bottleneck. If you give it to multiple people, they might make mistakes or stomp on each others' work accidentally. The best way is to divide up the config file and give parts of it to different -people. Ideally, we would delegate authority for *groups* of repos, not -individual repos, otherwise it doesn't scale. +people. -It would also be nice if we could specify what repos can be delegated to a -particular admin, and prevent him/her from specifying access control for any -other repos. This would be a nice "security" feature. +Ideally, we would delegate authority for *groups* of repos, not individual +repos, otherwise it doesn't scale. It would also be nice if we could prevent +an admin from creating access rules for *any* repo in the system -- i.e., set +limits on what repos he can control. This would be a nice "security" feature. + +Delegation offers a way to do all that. Note that delegated admins cannot +create or remove users, not can they define new repos. They can only define +access control rules for a set of repos they have been given authority for. ### splitting up the config file into fragments +It's easier to show how it all works with an example instead of long +descriptions. + To start with, recall that gitolite allows you to specify **groups** (of users or repos, same syntax). So the basic idea is that the main config file (`~/.gitolite/conf/gitolite.conf` by default) will specify some repo groups: @@ -98,13 +105,19 @@ of malware ;-) RW webserver_repos = bob RW malware_repos = mallory -As you can see, for each repo group you want to delegate authority over, -there's a **branch** in the `gitolite-admin` repo with the same name. Whoever -has write access to that branch, is allowed to define rules for repos in the -corresponding "repo group". + # you need these lines too -- they define what repos alice/bob/mallory are + # allowed to control + @webbrowser_repos = firefox lynx + @webserver_repos = apache nginx + @malware_repos = conficker storm -In other words, **we use gitolite's per-branch permissions to "enforce" the -separation between the delegated configs!** +**As you can see, for each repo group you want to delegate authority over, +there's a *branch* in the `gitolite-admin` repo with the same name. If you +have write access to that branch, you are allowed to define rules for repos in +that repo group.** + +In other words, we use gitolite's per-branch permissions to "enforce" the +separation between the delegated configs! Here's how to use this in practice: From f883fe7d717bb8184501aa3c310cbf8e81ba0ee1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 5 Oct 2009 20:21:33 +0530 Subject: [PATCH 089/850] compile: comments+efficiency - add better comments on the 2 main hashes - work around an inefficiency caused by the exclude prep code needing a list instead of a hash at a certain place --- src/gl-compile-conf | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 8f97bf0..ad50c4a 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -68,10 +68,30 @@ my $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pt my $REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern my $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern -# groups can now represent user groups or repo groups +# groups can now represent user groups or repo groups. + +# $groups{group}{member} = "master" (or name of fragment file in which the +# group is defined). my %groups = (); + +# %repos has two functions. + +# $repos{repo}{R|W}{user} = 1 if user has R (or W) permissions for at least +# one branch in repo. This is used by the "level 1 check" (see faq) + +# $repos{repo}{user} is a list of {ref, perms} pairs. This is used by the +# level 2 check. In order to allow "exclude" rules, the order of rules now +# matters, so what used to be entirely "hash of hash of hash" now has a list +# in between :) my %repos = (); -my %user_list = (); # only to catch lint; search for "lint" below + +# ... having been forced to use a list as described above, we lose some +# efficiency due to the possibility of the same {ref, perms} pair showing up +# multiple times for the same repo+user. So... +my %rurp_seen = (); + +# catch usernames<->pubkeys mismatches; search for "lint" below +my %user_list = (); # set the umask before creating any files umask($REPO_UMASK); @@ -210,7 +230,8 @@ sub parse_conf_file # for 2nd level check, store each "ref, perms" pair in order for my $ref (@refs) { - push @{ $repos{$repo}{$user} }, { $ref => $perms }; + push @{ $repos{$repo}{$user} }, { $ref => $perms } + unless $rurp_seen{$repo}{$user}{$ref}{$perms}++; } } } From 410c9ba46c5fa00e88579c9f9494c12ea7a67d0f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 7 Oct 2009 12:33:49 +0530 Subject: [PATCH 090/850] doc/install: add missing "cd" --- doc/0-INSTALL.mkd | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 9d92ce9..c54c7eb 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -20,6 +20,7 @@ tar file of the branch you want. Please use the make command, not a plain "git archive". The comments in the `Makefile` will explain why. git clone git://github.com/sitaramc/gitolite.git + cd gitolite make master.tar # or maybe "make rebel.tar" or "make pu.tar" From 9d2c9662a21044576d7ef4361d8bc22bc85deea9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 10 Oct 2009 12:52:40 +0530 Subject: [PATCH 091/850] install: can't assume p-t-a is setup! make installing the p-u hook conditional to avoid ugly error --- src/install.pl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/install.pl b/src/install.pl index 017854f..3e1f655 100755 --- a/src/install.pl +++ b/src/install.pl @@ -66,7 +66,10 @@ for my $repo (`find . -type d -name "*.git"`) { } # oh and one of those repos is a bit more special and has an extra hook :) -system("cp $GL_ADMINDIR/src/pta-hook.sh gitolite-admin.git/hooks/post-update"); -system("perl", "-i", "-p", "-e", "s(export GL_ADMINDIR=.*)(export GL_ADMINDIR=$GL_ADMINDIR)", - "gitolite-admin.git/hooks/post-update"); -chmod 0755, "gitolite-admin.git/hooks/post-update"; +if ( -d "gitolite-admin.git/hooks" ) { + print STDERR "copying post-update hook to gitolite-admin repo...\n"; + system("cp -v $GL_ADMINDIR/src/pta-hook.sh gitolite-admin.git/hooks/post-update"); + system("perl", "-i", "-p", "-e", "s(export GL_ADMINDIR=.*)(export GL_ADMINDIR=$GL_ADMINDIR)", + "gitolite-admin.git/hooks/post-update"); + chmod 0755, "gitolite-admin.git/hooks/post-update"; +} From ccd8372bb30641cb63efbd5d2db495af92bcf266 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 10 Oct 2009 12:38:22 +0530 Subject: [PATCH 092/850] aa ha! easy install script! src/00-easy-install.sh does *everything* needed, and it's mostly self-documented --- README.mkd | 4 + doc/0-INSTALL.mkd | 52 ++++++++- src/00-easy-install.sh | 245 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+), 3 deletions(-) create mode 100755 src/00-easy-install.sh diff --git a/README.mkd b/README.mkd index 5a59bd1..fe85675 100644 --- a/README.mkd +++ b/README.mkd @@ -3,6 +3,10 @@ > [IMPORTANT: There is now an "upgrade" document in the "doc" directory; > please read if upgrading gitolite] +> [Update 2009-10-10: apart from all the nifty new features, there's now an +> "easy install" script in the src directory. Please see the INSTALL +> document in the doc directory for details] + ---- Gitolite is the bare essentials of gitosis, with a completely different diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index c54c7eb..d4f3b44 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -1,6 +1,52 @@ # installing gitolite -### pre-requisites +### easy install + +There is now an easy install script that makes installing very easy for the +common case. **This script is meant to be run on your workstation, not on the +server!** It will take care of all the server side work, *and* get you +"push-to-admin" too :-) In short, it does **everything**! + +Assumptions: + + * you have a server to host gitolite + * git is installed on that server (and so is perl) + * you have a userid on that server + * you have ssh-pubkey (password-less) login to that userid + * (if you have only password access, run `ssh-keygen -t rsa` to create a + new keypair if needed, then run `ssh-copy-id user@host`) + * you have a clone or an archive of gitolite somewhere on your workstation + +If so, just `cd` to that clone and run `src/00-easy-install.sh` and follow the +prompts! (Running it without any arguments shows you usage plus other useful +info). + +#### advantages over the older install methods + + * all ssh problems reduced to **just one pre-requisite**: enable ssh pubkey + (password-less) access to the server from your workstation first + * the script takes care of all the server side work + * when done: + * you get two different pubkeys (the original one for command line + access as before, plus a new one, created by the script, for gitolite + access) + * you can admin gitolite by commit+push a "gitolite-admin" repo, just + like gitosis (i.e., full "push to admin" power!) + +#### disadvantages + + * has been tested only with Linux. However, the script now makes a much + better "document" on what actually needs to be done, so people installing + on non-Linux machines can probably follow the steps in the script and + install if they wish. Sort of "simulate" it... :) + +### manual install + +If for some reason you cannot use the easy-install method, (for example, +you're on a non-Linux machine) read on. Unlike the easy install, all the +below stuff is meant to be run on the server. + +#### pre-requisites on the server If you managed to install git, you might already have what gitolite needs: @@ -13,7 +59,7 @@ A major objective is to allow use by people without root access, permissions to create other userids, etc. Even if you have root, please add a user just for gitolite and do all this from that user. -### getting a tar file from a clone +#### getting a tar file from a clone You can clone the repo from github, then execute a make command to extract a tar file of the branch you want. Please use the make command, not a plain @@ -24,7 +70,7 @@ tar file of the branch you want. Please use the make command, not a plain make master.tar # or maybe "make rebel.tar" or "make pu.tar" -### quick install from tar file +#### install from tar file * make a temp directory somewhere, cd to it, and unpack the tar file * run `src/install.pl` and follow the prompts diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh new file mode 100755 index 0000000..54cb5a0 --- /dev/null +++ b/src/00-easy-install.sh @@ -0,0 +1,245 @@ +#!/bin/bash + +# easy install for gitolite + +# this is the client side piece. This gets run *before* the server side piece + +# run without any arguments for "usage" info + +# important setting: bail on any errors (else we have to check every single +# command!) +set -e + +die() { echo "$@"; echo "run $0 again without any arguments for help and tips"; exit 1; } +prompt() { + echo + echo + echo ------------------------------------------------------------------------ + echo "$1" + echo + read -p '...press enter to continue or Ctrl-C to bail out' +} +usage() { + cat </dev/null || + die "cant find at least some files in gitolite sources/config; aborting" + +# do we have pubkey auth on the server +ssh -o PasswordAuthentication=no $user@$host pwd >/dev/null || + die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something" + +# can the "gitolite-admin" repo be safely created in $HOME +[[ -d $HOME/gitolite-admin ]] && + die "please delete or move aside the \$HOME/gitolite-admin directory" + +# cool; now let's create a new key for you as a "gitolite user" (as opposed to +# a gitolite admin who needs to login to the server and get a command line) + +[[ -f $HOME/.ssh/$admin_name.pub ]] && die "pubkey $HOME/.ssh/$admin_name.pub exists; can't proceed" +prompt "the next command will create a new keypair for your gitolite access + + The pubkey will be $HOME/.ssh/$admin_name.pub. You will have to + choose a passphrase or hit enter for none. I recommend not having a + passphrase for now, and adding one with 'ssh-keygen -p' *as soon as* + all the setup is done and you've successfully cloned and pushed the + gitolite-admin repo. + + After that, I suggest you (1) install 'keychain' or something + similar, and (2) add the following command to your bashrc (since + this is a non-default key) + + ssh-add \$HOME/.ssh/$admin_name + + This makes using passphrases very convenient." + +ssh-keygen -t rsa -f $HOME/.ssh/$admin_name || die "ssh-keygen failed for some reason..." + +if [[ -n $SSH_AGENT_PID ]] +then + prompt "you're running ssh-agent. We'll try and do an ssh-add of the + private key we just created, otherwise this key won't get picked up. If + you specified a passphrase in the previous step, you'll get asked for one + now -- type in the same one." + + ssh-add $HOME/.ssh/$admin_name +fi + +# ok the gitolite key is done; create a stanza for it in ~/.ssh/config +echo " +host gitolite + hostname $host + user $user + identityfile ~/.ssh/$admin_name" > $HOME/.ssh/.gl-stanza + +if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null +then + prompt "your \$HOME/.ssh/config already has settings for gitolite. I will + assume they're correct, but if they're not, please edit that file, delete + that paragraph (that line and the following few lines), and rerun. + + In case you want to check right now (from another terminal) if they're + correct, here's what they are *supposed* to look like: +$(cat ~/.ssh/.gl-stanza)" + +else + prompt "creating settings for your gitolite access in $HOME/.ssh/config; + these are the lines that will be appended to your ~/.ssh/config: +$(cat ~/.ssh/.gl-stanza)" + + cat $HOME/.ssh/.gl-stanza >> $HOME/.ssh/config + # if the file didn't exist at all, it might have the wrong permissions + chmod 644 $HOME/.ssh/config +fi +rm $HOME/.ssh/.gl-stanza + +# ---------------------------------------------------------------------- +# client side stuff almost done; server side now +# ---------------------------------------------------------------------- + +# setup the gitolite sources and conf on the server +ssh $user@$host mkdir -p gitolite-install +rsync -a src conf doc $user@$host:gitolite-install/ + +# give the user an opportunity to change the rc +cp conf/example.gitolite.rc .gitolite.rc + # hey here it means "release candidate" ;-) + +prompt "the gitolite rc file needs to be edited by hand. The defaults +are sensible, so if you wish, you can just exit the editor. + +Otherwise, make any changes you wish and save it. Read the comments to +understand what is what -- the rc file's documentation is inline. + +Please remember this file will actually be copied to the server, and +that all the paths etc. represent paths on the server!" + +${VISUAL:-${EDITOR:-vi}} .gitolite.rc + +# copy the rc across +scp .gitolite.rc $user@$host: + +prompt "ignore any 'please edit this file' or 'run this command' type +lines in the next set of command outputs coming up. They're only +relevant for a manual install, not this one..." + +# extract the GL_ADMINDIR and REPO_BASE locations +GL_ADMINDIR=$(ssh $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") +REPO_BASE=$( ssh $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") + +# run the install script on the server +ssh $user@$host "cd gitolite-install; src/install.pl" + +# setup the initial config file +echo "#gitolite conf +#please see conf/example.conf for details on syntax and features + +repo gitolite-admin + RW+ = $admin_name + +repo testing + RW+ = @all + +" > gitolite.conf + +# send the config and the key to the remote +scp gitolite.conf $user@$host:$GL_ADMINDIR/conf/ + +scp $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir + +# run the compile script on the server +ssh $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf" + +# ---------------------------------------------------------------------- +# hey lets go the whole hog on this; setup push-to-admin! +# ---------------------------------------------------------------------- + +# setup the initial commit for the admin repo +echo "cd $REPO_BASE/gitolite-admin.git +GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir +GIT_WORK_TREE=$GL_ADMINDIR git commit -am start +" | ssh $user@$host + +ssh $user@$host "cd gitolite-install; src/install.pl" + +prompt "now we will clone the gitolite-admin repo to your workstation +and see if it all hangs together. We'll do this in your \$HOME for now, +and you can move it elsewhere later if you wish to." + +cd $HOME +git clone gitolite:gitolite-admin.git + +echo +echo +echo ------------------------------------------------------------------------ +echo "Cool -- we're done. Now you can edit the config file (currently +in ~/gitolite-admin/conf/gitolite.conf) to add more repos, users, etc. +When done, 'git add' the changed files, 'git commit' and 'git push'. + +Read the comments in conf/example.conf for information about the config +file format -- like the rc file, this also has inline documentation. + +Your URL for cloning any repo on this server will be + + gitolite:reponame.git + +However, any other users you set up will have to use + + $user@$host:reponame.git + +unless they also create similar settings in their '.ssh/config' file." From d0d9cbe3afebc490f230fb6fa9ea0420eb1fcd1a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Oct 2009 05:51:19 +0530 Subject: [PATCH 093/850] easy install comment about clientside/serverside was wrong --- src/00-easy-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 54cb5a0..d19c1a5 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -2,7 +2,8 @@ # easy install for gitolite -# this is the client side piece. This gets run *before* the server side piece +# this runs on the client side, and itself takes care of all the server side +# work. You don't have to do anything on the server side directly # run without any arguments for "usage" info From d78bbe8c3eecc9b3592c1b1387b784571286e5f1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Oct 2009 08:31:59 +0530 Subject: [PATCH 094/850] lots of doc changes reflecting "push to admin" is default now :) - added comments to easy install to help do it manually - README: some stuff moved to tips doc, brief summary of extras (over gitosis) added - INSTALL: major revamp, easy install and manual install, much shorter and much more readable! plus other docs changed as needed, and updated the tips doc to roll in some details from "update.mkd" in the "ml" branch --- README.mkd | 104 ++++++++++++------------------- conf/example.gitolite.rc | 2 +- doc/0-INSTALL.mkd | 127 +++++++++----------------------------- doc/0-UPGRADE.mkd | 40 ++++++++++-- doc/1-migrate.mkd | 63 ++++++++----------- doc/2-admin.mkd | 55 +++-------------- doc/3-faq-tips-etc.mkd | 130 +++++++++++++++++++++++---------------- doc/4-push-to-admin.mkd | 5 ++ src/00-easy-install.sh | 99 +++++++++++++++++++++++++---- 9 files changed, 309 insertions(+), 316 deletions(-) diff --git a/README.mkd b/README.mkd index fe85675..7cbbaf3 100644 --- a/README.mkd +++ b/README.mkd @@ -9,17 +9,16 @@ ---- -Gitolite is the bare essentials of gitosis, with a completely different -config file that allows (at last!) access control down to the branch level, -including specifying who can and cannot *rewind* a given branch. It is -released under GPL v2. See COPYING for details. +Gitolite is a rewrite of gitosis, with a completely different config file that +allows (at last!) access control down to the branch level, including +specifying who can and cannot *rewind* a given branch. In this document: * why - * what's gone - * what's new - * the workflow + * what's extra + * security + * contact and license ---- @@ -32,30 +31,15 @@ a typical $DAYJOB setting, there are some issues: and be done * often, "python-setuptools" isn't installed (and on a Solaris9 I was trying to help remotely, we never did manage to install it eventually) - * or you don't have root access, or the ability to add users + * you don't have root access, or the ability to add users (this is also true + for people who have just one userid on a hosting provider) * the most requested feature (see "what's new?") had to be written anyway -### what's gone - -While I was pondering the need to finally learn python[1] , I also realised -that: - - * no one in $DAYJOB type environments will use or approve access methods - that work without any authentication, so I didn't need gitweb/daemon - support in the tool or in the config file. - - Update 2009-09-24: I don't use this feature but someone wanted it, so I - added it... see the "faq, tips, etc" document for more - - * the idea that you admin it by pushing to a special repo is nice, but not - really necessary because of how rarely these changes are made, especially - considering how much code is involved in that piece - All of this pointed to a rewrite. In perl, naturally :-) -### what's new +### what's extra -Per-branch permissions. You will not believe how often I am asked this at +**Per-branch permissions**. You will not believe how often I am asked this at $DAYJOB. This is almost the single reason I started *thinking* about rolling my own gitosis in the first place. @@ -65,50 +49,42 @@ deleting a branch (which is really just an extreme form of rewind). I needed something in between allowing anyone to do it (the default) and disabling it completely (`receive.denyNonFastForwards` or `receive.denyDeletes`). -Take a look at the example config file in the repo to see how I do this. I -copied the basic idea from `update-hook-example.txt` (it's one of the "howto"s -that come with the git source tree). However, please note the difference in -the size and complexity of the *operational code* between the update hook in -that example, and in mine :-) The reason is in the next section. +Here're **some more features**. All of them are documented in detail +somewhere in the `doc/` subdirectory. -### the workflow + * simpler, yet far more powerful, config file syntax, including specifying + gitweb/daemon access. You'll need this power if you manage lots of users + + repos + combinations of access + * config file syntax gets checked upfront, and much more thoroughly + * if your requirements are still too complex, you can split up the config + file and delegate authority over parts of it + * more comprehensive logging [aka: management does not think "blame" is just + a synonym for "annotate" :-)] + * "personal namespace" prefix for each dev + * migration guide and simple converter for gitosis conf file + * "exclude" (or "deny" rights in the config file) -- this is the "rebel" + branch in the repository, and always will be ;-) -In order to get per-branch access, you *must* use an update hook. However, -that only gets invoked on a push; "read" access still has to be controlled -right at the beginning, before git even enters the scene (just the way gitosis -currently works). +### security -So: either split the access control into two config files, or have two -completely different programs *both* parse the same one and pick what they -want. Crap... I definitely don't want the hook doing any parsing, (and it -would be nice if the auth-control program didn't have to either). +Due to the environment in which this was created and the need it fills, I +consider this a "security" program, albeit a very modest one. The code is +very small and easily reviewable -- the 2 programs that actually control +access when a user logs in total about 200 lines of code (about +80 lines according to "sloccount"). -So I changed the workflow completely: +For the first person to find a security hole in it, defined as allowing a +normal user (not the gitolite admin) to read a repo, or write/rewind a ref, +that the config file says he shouldn't, and caused by a bug in *code* that is +in the "master" branch, (not in the other branches, or the configuration file +or in Unix, perl, shell, etc.)... well I can't afford 1000 USD rewards like +djb, so you'll have to settle for 1000 INR (Indian Rupees) as a "token" prize +:-) - * all admin changes happen *on the server*, in a special directory that - contains the config and the users' pubkeys. But there's no commit and - push afterward - * instead, after making changes, you "compile" the configuration. This - refreshes `~/.ssh/authorized_keys`, as well as puts a parsed form of the - access list in a file for the other two pieces to use. +---- -The pre-parsed form is basically a huge perl variable. It's human readable -too (never mind what the python guys say!) +### contact and license -So the admin knows immediately if the config file had any problems, which is -good. Also, the relatively complex parse code is not part of the actual -access control points, which are: - - * the program that is run via `~/.ssh/authorized_keys` (I call it - `gl-auth-command`, equivalent to `gitosis-serve`); this decides whether - git should even be allowed to run (basic R/W/no access) - * the update-hook on each repo, which decides the per-branch permissions - -### footnotes - -[1] I hate whitespace to mean anything significant except for text; this is a -personal opinion *only*, so pythonistas please back off :-) - -### contact +Gitolite is released under GPL v2. See COPYING for details. sitaramc@gmail.com diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index ea80bec..700fc0e 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -5,7 +5,7 @@ # this file is meant to be pulled into a perl program using "do" or "require". # You do NOT need to know perl to edit the paths; it should be fairly -# self-explanatory +# self-explanatory and easy to maintain perl syntax :-) # -------------------------------------- diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index d4f3b44..5ef38cf 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -1,9 +1,24 @@ # installing gitolite +This document tells you how to install gitolite. After the install is done, +you may want to see the "admin" document for adding users, repos, etc. + +There's an easy install script for Linux, and for other Unixes there's a +slightly more manual process. Both are explained here. + +In this document: + + * easy install + * manual install + * other notes + * next steps + +---- + ### easy install -There is now an easy install script that makes installing very easy for the -common case. **This script is meant to be run on your workstation, not on the +There is an easy install script that makes installing very easy for the common +case. **This script is meant to be run on your workstation, not on the server!** It will take care of all the server side work, *and* get you "push-to-admin" too :-) In short, it does **everything**! @@ -35,110 +50,26 @@ info). #### disadvantages - * has been tested only with Linux. However, the script now makes a much - better "document" on what actually needs to be done, so people installing - on non-Linux machines can probably follow the steps in the script and - install if they wish. Sort of "simulate" it... :) + * has been tested only with Linux ### manual install If for some reason you cannot use the easy-install method, (for example, -you're on a non-Linux machine) read on. Unlike the easy install, all the -below stuff is meant to be run on the server. +you're on a non-Linux machine), it's not very complicated. Just open the file +`src/00-easy-install.sh` in a nice, syntax coloring, text editor, and follow +the instructions marked "MANUAL" :-) -#### pre-requisites on the server +### other notes -If you managed to install git, you might already have what gitolite needs: - - * git itself, the more recent the better - * perl, typically installed with git, since git sort of needs it; any - version that includes `Data::Dumper`[1] will do. - * one user account on the server, with password access [2] - -A major objective is to allow use by people without root access, permissions -to create other userids, etc. Even if you have root, please add a user just -for gitolite and do all this from that user. - -#### getting a tar file from a clone - -You can clone the repo from github, then execute a make command to extract a -tar file of the branch you want. Please use the make command, not a plain -"git archive". The comments in the `Makefile` will explain why. - - git clone git://github.com/sitaramc/gitolite.git - cd gitolite - make master.tar - # or maybe "make rebel.tar" or "make pu.tar" - -#### install from tar file - - * make a temp directory somewhere, cd to it, and unpack the tar file - * run `src/install.pl` and follow the prompts - -**When you are told to edit some file, please read the comments in the file**. -And if you can make some time to read the documentation, please do. -Especially if you have problems. - -Notes: - - * At present the location of `~/.gitolite.rc` is fixed (maybe later I'll - change it to a "git config" variable but I don't see much need right now) - - If you edit it and change any paths, be sure to keep the perl syntax -- - you *don't* have to know perl to do so, it's fairly easy to guess in this - limited case. And of course, make sure you adjust the commands shown - above to suit the new locations + * If you edit `~/.gitolite.rc` and change any paths, be sure to keep the + perl syntax -- you *don't* have to know perl to do so, it's fairly easy to + guess in this limited case * the config file is (by default) at `~/.gitolite/conf/gitolite.conf`, though you can change its location in the "rc" file. Edit the file as you - wish. The comments in the file ought to be clear enough but let me know - if not + wish. The comments in the example file (`conf/example.conf`) ought to be + clear enough but let me know if not - * if you want to bring in existing (bare, server) repos into gitolite, this - should work (refer to `~/.gitolite.rc` for *your* values of the pathnames - below): - * backup the repo, then move it to `$BASE_REPO` - * copy `$GL_ADMINDIR/src/update-hook.pl` to - `[reponame].git/hooks/update` -- if you don't do this, per branch - restrictions will not work - * then update the keys and the config file and "compile" (see "admin" - document) +### next steps -### Footnotes: - -[1] Actually, due to the way gitolite is architected, you can manage -without `Data::Dumper` on the server if you have no choice. Only -`gl-compile-conf` needs it, so just run that on some other machine and copy -the two output files across. Cumbersome but doable... the advantage of -separating all the hard work into a manually-run piece :) - -[2] If you have *only* pubkey access, and **no** password access, then your -pubkey is already in the server's `~/.ssh/authorized_keys`. If you also need -to access git as a developer (clone, push, etc), do *not* submit this same -pubkey to gitolite -- it won't work. - -Instead, create a different keypair for your "developer" role (by, e.g., -`ssh-keygen -t rsa -f ~/.ssh/gitdev`), then give `~/.ssh/gitdev.pub` to -gitolite as "yourname.pub", just like you would do for any other user. - -Then you create a suitable `~/.ssh/config` to use the correct key -automatically, something like this: - - host gitadm - hostname my.server - user my_userid_on_server - - host gitdev - hostname my.server - user my_userid_on_server - identityfile ~/.ssh/gitdev - -From now on, `ssh gitadm` will get you a command line on the server, to do -gitolite admin and other work. And your repository URLs would look like -`gitdev:reponame.git`. Very, very, simple... - -And as with gitosis, there's more "ssh" magic than "git" magic here :-) - ----- - -gitolite is released under the GPL v2 license. See COPYING for details +See the "admin" document for how to add users, etc. diff --git a/doc/0-UPGRADE.mkd b/doc/0-UPGRADE.mkd index c106bc7..5e242f6 100644 --- a/doc/0-UPGRADE.mkd +++ b/doc/0-UPGRADE.mkd @@ -1,11 +1,19 @@ # upgrading gitolite atomically +Upgrading is done **manually, on the server** (except the last step, which is +on your admin repo clone), even if you installed it using the easy install +script on the client. First, it's not as difficult as an install so you don't +really need a script. Second, you may have customised the "rc" file +(`~/.gitolite.rc` on the server) and I'm reluctant to mess with that in an +automated way. + ### general upgrade notes If you follow the steps below, you can make the upgrade "atomic", so you don't have to do it at a "quiet" time or something. -1. untar the new version to some temp directory and `cd` to it +1. copy a tar file containing the new version to the server, untar it to some + temp directory and `cd` to it 2. *prepare* the new version of `~/.gitolite.rc`. It **must** have **all** the variables defined in `conf/example.gitolite.rc` (the "new" rc file), @@ -31,12 +39,11 @@ have to do it at a "quiet" time or something. src/install.pl 5. compile the config once again, in case the *internal* format of the - compiled config file (`$GL_CONF_COMPILED`) has changed + compiled config file (`$GL_CONF_COMPILED`) has changed. - src/gl-compile-conf - - (if you've already setup "push-to-admin", this step should be replaced by - a "git push". Make a dummy commit if needed, to make the push happen). + To do this, you have to do a "git push" on the client side. That might + require a dummy change (maybe add a blank line somewhere) because + otherwise the push will not happen. And you're done. @@ -45,6 +52,27 @@ And you're done. If any extra steps beyond the generic ones above are needed, they will be listed here, newest first. +#### upgrading from 410c9ba + +Between 410c9ba and this version, gitolite managed to make "push to admin" the +default for new installs, but in a much more painless way. If you're +upgrading, you're not forced to use "push to admin", but I'd suggest you: + + * make sure you have password-less (pubkey) login to a command line on your + server + * save your `~/.gitolite.rc`, `keydir/*.pub` and your `conf/gitolite.conf` + files from the server, bring them to your workstation + * then run `src/00-easy-install.sh` on the workstation, as if it were a + fresh install + * when the editor pops up to edit the rc file, delete all the lines in + it and copy them from the saved `~/.gitolite.rc` + * at the end of the script, after the gitolite-admin repo has been + cloned successfully, copy the saved `conf/gitolite.conf` and + `keydir/*.pub` to the clone, then add, commit, and push + +Gitolite also learnt to delegate parts of the config to other users. See +`doc/5-delegation.mkd` for details. + #### upgrading from 8217ef9 Between 8217ef9 and this version, gitolite learnt to handle gitweb/daemon diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd index fe0b4cf..fb0491f 100644 --- a/doc/1-migrate.mkd +++ b/doc/1-migrate.mkd @@ -3,14 +3,9 @@ [TODO: make the migration tool fix up gitweb and daemon control also...] Migrating from gitosis to gitolite is pretty easy, because the basic design is -the same. The differences are: +the same. - * gitolite does not use a special repo for the configuration, pubkeys, etc. - You can choose to version that directory but it is not required that you - do so - -Here's how we migrated my work repos (note: substitute real paths, from your -`~/.gitolite.rc`, for `$REPO_BASE` and `$GL_ADMINDIR` below): +Here's how we migrated my work repos: 1. login as the `git` user on the server, and get a bash shell prompt @@ -18,10 +13,17 @@ Here's how we migrated my work repos (note: substitute real paths, from your else. This will prevent users from pushing anything while you do the backup, migration, etc. -3. For added safety, **delete** the post-update hook that gitosis-admin +3. **edit** `~/.ssh/authorized_keys` and **carefully** remove all the lines + containing "gitosis-serve", as well as the marker line that says + "auto-generated by gitosis, DO NOT REMOVE", then save the file. If the + file did not have any other keys and is now empty, don't worry -- save it + anyway because gitolite expects the file to be present (even if it is + empty). + +4. For added safety, **delete** the post-update hook that gitosis-admin installed - rm $REPO_BASE/gitosis-admin.git/hooks/post-update + rm ~/repositories/gitosis-admin.git/hooks/post-update or at least rename it to `.sample` like all the other hooks hanging around, or edit it and comment out the line that calls `gitosis-run-hook @@ -30,39 +32,34 @@ Here's how we migrated my work repos (note: substitute real paths, from your If you do not do this, an accidental push to the gitosis-admin repo will mess up your `~/.ssh/authorized_keys` file -4. take a **backup** of the `$REPO_BASE` directory +5. take a **backup** of the `~/repositories` directory -5. untar gitolite to some temporary directory and follow the instructions to - **install** it using `src/install.pl` +Now, log off the server and get back to the client: -6. **convert** your gitosis config file: +1. follow instructions to install gitolite; see install document. Make sure + that you **don't** change the default path for `$REPO_BASE`! - cd $GL_ADMINDIR - src/conf-convert.pl < ~/.gitosis.conf > conf/gitolite.conf +2. **convert** your gitosis config file. Substitute the path for your + gitosis-admin clone in `$GSAC` below, and similarly the path for your + gito**lite**-admin clone in `$GLAC` - be sure to check the file to make sure it converted correctly + src/conf-convert.pl < $GSAC/gitosis.conf > $GLAC/gitolite.conf -7. **copy** the update hook to each of the existing repos (if you have repos - in subdirectories, this won't work as is; adapt it): + Be sure to check the file to make sure it converted correctly - for i in $REPO_BASE/*.git - do - cp src/update-hook.pl $i/hooks/update - done +3. **copy** the keys from gitosis's keydir (same meanings for GSAC and GLAC) -8. **copy** the keys from gitosis's keydir + cp $GSAC/keydir/* $GLAC/keydir - cp $REPO_BASE/gitosis-admin.git/gitosis-export/keydir/* keydir - -9. **Important: expand** any multi-key files you may have. See the "faq, +4. **Important: expand any multi-key files you may have**. See the "faq, tips, etc" document in the doc directory for an explanation of what multi-keys are, how gitosis does them and how gitolite does it differently. You can split the keys manually, or use the following code (just - copy-paste it into your xterm): + copy-paste it into your xterm after "cd"-ing to your gitolite-admin repo + clone): - cd $GL_ADMINDIR wc -l keydir/*.pub | grep -v total | grep -v -w 1 | while read a b do i=1 @@ -82,12 +79,4 @@ Here's how we migrated my work repos (note: substitute real paths, from your "sitaram@laptop.pub" and "sitaram@desktop.pub" or whatever. *Please check the files to make sure this worked properly* -10. **edit** `~/.ssh/authorized_keys` and **carefully** remove all the lines - containing "gitosis-serve", as well as the marker line that says - "auto-generated by gitosis, DO NOT REMOVE", then save the file. If the - file did not have any other keys and is now empty, don't worry -- save it - anyway because gitolite expects the file to be present (even if it is - empty). - -At this point you're ready to "compile" the configuration. See the "admin" -document for what to do, and how to check the outputs, etc. +5. Check all your changes to your gitolite-admin clone, commit, and push diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 8cd2216..133455a 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -22,15 +22,15 @@ Please read on to see how to do this correctly. extension, like `sitaram.pub` or `john-smith.pub`. You can also use periods and underscores - * copy all these `*.pub` files to `$GL_KEYDIR` + * copy all these `*.pub` files to `keydir` in your gitolite-admin repo clone - * the config file (`$GL_CONF`) is very well commented, please take a couple - of minutes to read it. Then edit it and + * edit the config file (`conf/gitolite.conf` in your admin repo clone). See + `conf/example.conf` in the gitolite source for details on what goes in + that file, syntax, etc. Just add new repos as needed, and add new users + and give them permissions as required. The users names should be exactly + the same as their keyfile names, but without the `.pub` extension - * add new repos as needed - * add new users and give them permissions as required. The users names - should be exactly the same as their keyfile names, but without the - `.pub` extension + * when done, commit your changes and push #### specifying gitweb and daemon access @@ -51,41 +51,6 @@ one-time setup you must do separately. All this does is: value you specified for `$projects_list` when setting up gitweb) * for daemon, create the file `git-daemon-export-ok` in the repository -`src/gl-compile-conf` will keep these files consistent with the config -settings -- this includes removing such settings if you remove "read" -permissions for the special usernames. - -#### compiling - - * backup your `~/.ssh/authorized_keys` file if you feel nervous :-) - * that's "backup" as in "copy", not "move". The next step won't work if - the file doesn't exist. Even an empty one is fine but it must be - present - * if you don't have an `~/.ssh/authorized_keys` file at all, you may - have logged in with a password, which in turn might mean you are not - familiar with ssh and authkeys etc. If so, please read up at least - [this](http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh), - and preferably also the man pages for sshd and sshd\_config, to make - sure you understand the security implications of what you are doing. - Once you have understood that, create at least an empty - `~/.ssh/authorized_keys` file before proceeding to the next step - - * cd to `$GL_ADMINDIR` and run `src/gl-compile-conf` - -That should be it, really. However, if you want to be doubly sure, or maybe -the first couple of times you use it, you may want to check these: - - * check the outputs - - * `~/.ssh/authorized_keys` should contain one line for each "user" pub - key added, between two "marker" lines (which you should please please - not remove!). The line should contain a "command=" pointing to a - `$GL_ADMINDIR/src/gl-auth-command` file, then some sshd restrictions, the - key, etc. - * `$GL_CONF_COMPILED` should contain an expanded list of the access - control rules. It may look a little long, but it's fairly intuitive! - - * if the run threw up any "initialising empty repo" messages, check the - individual repos (inside `$REPO_BASE`) if you wish. Especially make sure - the `$REPO_BASE/[reponame].git/hooks/update` got copied OK and is - executable +The "compile" script will keep these files consistent with the config settings +-- this includes removing such settings if you remove "read" permissions for +the special usernames. diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 66ab956..f24d30c 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -5,19 +5,21 @@ In this document: * common errors and mistakes * git version dependency * other errors, warnings, notes... + * getting a tar file from a clone * differences from gitosis * simpler syntax * two levels of access rights checking * error checking the config file * delegating parts of the config file * easier to specify gitweb/daemon access - * built-in logging + * better logging * one user, many keys * who am I? * other cool things - * developer specific branches + * "personal" branches * design choices * why we don't do "excludes" + * keeping the parser and the access control separate ### common errors and mistakes @@ -37,10 +39,10 @@ In this document: Here's a workaround for a version dependency that the normal flow of gitolite has. -When you edit your config file to create a new repo, and run -`src/gl-compile-conf`, gitolite creates an empty, bare repo for you. -Normally, you're expected to clone this on the client side, and start working --- make your first commit(s), then push, etc. +When you edit your config file to create a new repo, and push the changes to +the server, gitolite creates an empty, bare repo for you. Normally, you're +expected to clone this on the client side, and start working -- make your +first commit(s), then push, etc. However, cloning an empty repo requires a server side git version that is at least 1.6.2. Gitolite detects this when creating a repo, and warns you. @@ -74,21 +76,27 @@ normal way, since it's not empty anymore. * if you specify a repo that is not at the top level `$REPO_BASE`, be sure to manually create the intermediate directories first. For instance if - you specify a new repo called "a/b/c" to the config file and "compile", - the "compile" script will just `mkdir a/b/c.git`, assuming "a/b" has - already been created - - * if you run `git init` inside `$GL_ADMINDIR` (that is, make it a normal, - non-bare, repo), then, everytime you "compile" (run - `src/gl-compile-conf`), any changes to `conf` and `keydir` will - automatically be committed. This is a simple safety net in case you - accidentally delete the whole config or something. Also see - [4-push-to-admin.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/4-push-to-admin.mkd) - if you really know what you're doing and want "push to admin" + you specify a new repo called "a/b/c" to the config file and push, the + "compile" script will just `mkdir a/b/c.git`, assuming "a/b" has already + been created * gitweb not able to read your repos? You can change the umask for newly created repos to something more relaxed -- see the `~/.gitolite.rc` file +### getting a tar file from a clone + +You can clone the repo from github or indefero, then execute a make command to +extract a tar file of the branch you want. Please use the make command, not a +plain "git archive", because the Makefile adds a file called +`.GITOLITE-VERSION` that will help you identify which version you are using. + + git clone git://github.com/sitaramc/gitolite.git + # (OR) + git clone git://sitaramc.indefero.net/sitaramc/gitolite.git + cd gitolite + make master.tar + # or maybe "make rebel.tar" or "make pu.tar" + ### differences from gitosis Apart from the big ones listed in the top level README, and subjective ones @@ -176,9 +184,8 @@ gitosis does not do any. I just found out that if you mis-spell `members` as `member`, gitosis will silently ignore it, and leave you wondering why access was denied. -In gitolite, you have to "compile" the config file first (this step takes the -place of the commit+push in gitosis), and keyword typos *are* caught so you -know right away. +Gitolite "compiles" the config file first and keyword typos *are* caught so +you know right away. #### delegating parts of the config file @@ -219,24 +226,21 @@ bits and pieces. Here's an example, using short repo names for convenience: repo r2 # ...and so on... -#### built-in logging +### better logging -...just in case of emergency :-) +If you have been too liberal with the permission to rewind, it has built-in +logging as an emergency fallback if someone goes too far, or for audit +purposes [`*`]. The logfile names and location are configurable, and can +include the year/month/day etc in the filename for easy archival or further +processing. The log file even tells you which pattern in the config file +matched to allow that specific access to proceed. -Let's say you gave a dev the right to rewind a branch and he went and rewound -it all the way, or pushed something drastically different on it. Now you need -to recover the commit that got wiped out. +> [`*`] setting `core.logAllRefUpdates true` does provide a safety net +> against over-zealous rewinds, but it does not tell you "who". And +> strangely, management does not seem to share the view that "blame" is just +> a synonym for "annotate" ;-)] -If you'd remembered to `git config core.logAllRefUpdates` for that repo, or -globally, you'd be fine -- the reflog will tell you. Otherwise you'd be left -grubbing around in `git fsck --unreachable` a bit :-( - -And even if you recover the correct commit, you'll never know *who* did it -- -not unless you add a one-line patch to gitosis, plus a `post-receive` hook to -every repository. - -With gitolite, there's a log file in `$GL_ADMINDIR` that contains lines like -this: +The log lines look like this: 2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master @@ -283,33 +287,31 @@ In gitolite, it's simple: just ask nicely :-) ### other cool things -#### developer specific branches +### "personal" branches -So I know what gitolite calls me. Big deal... who cares? +"personal" branches are great for corporate environments, where +unauthenticated pull/clone is a no-no. Since a dev workstation cannot do +authentication, even work shared just between 2 devs has to go *via* the +server. This causes the same branch name clutter as in a centralised VCS, +plus setting up permissions for this becomes a chore for the admin. -Here is an idea: give every developer a personal "scratch" namespace within -which she can create, rewind, or delete any branch. For example, I would own -anything under +gitolite lets you define a "personal" or "scratch" namespace prefix for +each developer (e.g., `refs/personal//*`), with full +permissions for that dev and read-only for everyone else. And you get +this without adding a single line to the access config file -- pretty +much fire and forget as far as the admin is concerned, even if there is +constant churn in the project teams. - $PERSONAL_BRANCH_PREFIX/sitaram/ +Not bad for something that took just *one* line of code to implement. +And that's one clean, readable, line, by the way ;-) -The admin could set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate +The admin would set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate this to all users. It could be something like `refs/heads/personal`, which means all such branches will show up in `git branch` lookups and `git clone` will fetch them. Or he could use, say, `refs/personal`, which means it won't show up in any normal "branch-y" commands and stuff, and generally be much less noisy. -Yes, I know git is all about allowing private branches, but in a corporate -environment it's not always possible to pull from a co-worker, for the same -reasons you don't have anonymous access (like the git:// protocol). A normal -developer workstation cannot do authentication, so how would they know who's -pulling? This is a perfect way to share code *without* cluttering the global -namespace, and each developer controls his/her own set of branches! - -The amount of code needed? *One line!* I'll spend about 3x more on declaring -and initialising the new variable, and 30x more on documenting it :-) - **Note that a user who has NO write access cannot have personal branches**; if you read the section (above) on "two levels of access rights checking" you'll understand why. @@ -343,6 +345,9 @@ Just don't *show* the user this config file; it might sound insulting :-) #### why we don't do "excludes" +[umm... having said all this, I implemented it anyway; see the "rebel" +branch!] + I found an error in the example conf file. This snippet *seems* to say that "bruce" can write versioned tags (`refs/tags/v[0-9].*`), but the other staffers can't: @@ -387,3 +392,24 @@ The lack of overlap between refexes ensures ***no confusion*** in specifying, understanding, and ***auditing***, what is allowed and what is not. And in security, "no confusion" is a good thing :-) + +#### keeping the parser and the access control separate + +There are two programs concerned with access control: + + * `gl-auth-command`, the program that is run via `~/.ssh/authorized_keys`; + this decides whether git should even be allowed to run (basic R/W/no + access). (This one cannot decide on the branch-level access; it is not + known at this point what branch is being accessed) + * the update-hook on each repo, which decides the per-branch permissions + +I have chosen to keep the relatively complex task of parsing the config file +out of them to keep them simpler (and faster). So any changes to the config +have to be first "compiled", and the access control programs use this +"compiled" version of the config. (The compile step also refreshes +`~/.ssh/authorized_keys`). + +If you choose the "easy install" method, all this is quite transparent to you +anyway. If you cannot use the easy install and must install manually, I have +clear instructions on how to set it up. + diff --git a/doc/4-push-to-admin.mkd b/doc/4-push-to-admin.mkd index 48d1baa..8791480 100644 --- a/doc/4-push-to-admin.mkd +++ b/doc/4-push-to-admin.mkd @@ -1,5 +1,10 @@ # "push to admin" in gitolite +**WARNING: THIS DOCUMENT IS OBSOLETE. DO NOT USE. IT IS RETAINED ONLY FOR +HISTORICAL PURPOSES**. Gitolite now does "push-to-admin" by default, and does +it very easily and simply by front-loading the ssh problem. See the install +doc for details. + ---- Gitosis's default mode of admin is by cloning and pushing the `gitosis-admin` diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index d19c1a5..2ca8bf1 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -2,9 +2,13 @@ # easy install for gitolite -# this runs on the client side, and itself takes care of all the server side +# you run this on the client side, and it takes care of all the server side # work. You don't have to do anything on the server side directly +# to do a manual install (since I have tested this only on Linux), open this +# script in a nice, syntax coloring, text editor and follow the instructions +# prefixed by the word "MANUAL" in the comments below :-) + # run without any arguments for "usage" info # important setting: bail on any errors (else we have to check every single @@ -63,6 +67,9 @@ EOFU [[ "$1" =~ [^a-zA-Z0-9._-] ]] && die "user '$1' invalid" [[ "$3" =~ [^a-zA-Z0-9._-] ]] && die "admin_name '$3' invalid" +# MANUAL: (info) we'll use "git" as the user, "server" as the host, and +# "sitaram" as the admin_name in example commands shown below, if any + user=$1 host=$2 admin_name=$3 @@ -71,8 +78,9 @@ admin_name=$3 # basic sanity checks # ---------------------------------------------------------------------- -# are we in the right directory? We should have all the gitolite sources -# here... +# MANUAL: make sure you're in the gitolite directory, at the top level. +# The following files should all be visible: + ls src/gl-auth-command \ src/gl-compile-conf \ src/install.pl \ @@ -81,16 +89,23 @@ ls src/gl-auth-command \ conf/example.gitolite.rc >/dev/null || die "cant find at least some files in gitolite sources/config; aborting" -# do we have pubkey auth on the server +# MANUAL: make sure you have password-less (pubkey) auth on the server. That +# is, running "ssh git@server" should log in straight away, without asking for +# a password + ssh -o PasswordAuthentication=no $user@$host pwd >/dev/null || die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something" -# can the "gitolite-admin" repo be safely created in $HOME +# MANUAL: make sure there's no "gitolite-admin" directory in $HOME (actually +# for the manual flow this doesn't matter so much!) + [[ -d $HOME/gitolite-admin ]] && die "please delete or move aside the \$HOME/gitolite-admin directory" -# cool; now let's create a new key for you as a "gitolite user" (as opposed to -# a gitolite admin who needs to login to the server and get a command line) +# MANUAL: create a new key for you as a "gitolite user" (as opposed to you as +# the "gitolite admin" who needs to login to the server and get a command +# line). For example, "ssh-keygen -t rsa ~/.ssh/sitaram"; this would create +# two files in ~/.ssh (sitaram and sitaram.pub) [[ -f $HOME/.ssh/$admin_name.pub ]] && die "pubkey $HOME/.ssh/$admin_name.pub exists; can't proceed" prompt "the next command will create a new keypair for your gitolite access @@ -111,6 +126,15 @@ prompt "the next command will create a new keypair for your gitolite access ssh-keygen -t rsa -f $HOME/.ssh/$admin_name || die "ssh-keygen failed for some reason..." +# MANUAL: copy the pubkey created to the server, say to /tmp. This would be +# "scp ~/.ssh/sitaram.pub git@server:/tmp" (the script does this at a later +# stage, you do it now for convenience). Note: only the pubkey (sitaram.pub). +# Do NOT copy the ~/.ssh/sitaram file -- that is a private key! + +# MANUAL: if you're running ssh-agent (see if you have an environment variable +# called SSH_AGENT_PID in your "env"), you should add this new key. The +# command is "ssh-add ~/.ssh/sitaram" + if [[ -n $SSH_AGENT_PID ]] then prompt "you're running ssh-agent. We'll try and do an ssh-add of the @@ -121,7 +145,17 @@ then ssh-add $HOME/.ssh/$admin_name fi -# ok the gitolite key is done; create a stanza for it in ~/.ssh/config +# MANUAL: you now need to add some lines to the end of your ~/.ssh/config +# file. If the file doesn't exist, create it. Make sure the file is "chmod +# 644". + +# The lines to be included look like this: + +# host gitolite +# hostname server +# user git +# identityfile ~/.ssh/sitaram + echo " host gitolite hostname $host @@ -153,10 +187,22 @@ rm $HOME/.ssh/.gl-stanza # client side stuff almost done; server side now # ---------------------------------------------------------------------- -# setup the gitolite sources and conf on the server +# MANUAL: copy the gitolite directories "src", "conf", and "doc" to the +# server, to a directory called (for example) "gitolite-install". You may +# have to create the directory first. + ssh $user@$host mkdir -p gitolite-install rsync -a src conf doc $user@$host:gitolite-install/ +# MANUAL: now log on to the server (ssh git@server) and get a command line. +# This step is for your convenience; the script does it all from the client +# side but that may be too much typing for manual use ;-) + +# MANUAL: cd to the "gitolite-install" directory where the sources are. Then +# copy conf/example.gitolite.rc as ~/.gitolite.rc and edit it if you wish to +# change any paths. Make a note of the GL_ADMINDIR and REPO_BASE paths; you +# will need them later + # give the user an opportunity to change the rc cp conf/example.gitolite.rc .gitolite.rc # hey here it means "release candidate" ;-) @@ -183,10 +229,17 @@ relevant for a manual install, not this one..." GL_ADMINDIR=$(ssh $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") REPO_BASE=$( ssh $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") -# run the install script on the server +# MANUAL: still in the "gitolite-install" directory? Good. Run +# "src/install.pl" + ssh $user@$host "cd gitolite-install; src/install.pl" -# setup the initial config file +# MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf +# and add at least the following lines to it: + +# repo gitolite-admin +# RW+ = sitaram + echo "#gitolite conf #please see conf/example.conf for details on syntax and features @@ -203,28 +256,48 @@ scp gitolite.conf $user@$host:$GL_ADMINDIR/conf/ scp $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir -# run the compile script on the server +# MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" + ssh $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf" # ---------------------------------------------------------------------- # hey lets go the whole hog on this; setup push-to-admin! # ---------------------------------------------------------------------- -# setup the initial commit for the admin repo +# MANUAL: make the first commit in the admin repo. This is a little more +# complex, so read carefully and substitute the correct paths. What you have +# to do is: + +# cd $REPO_BASE/gitolite-admin.git +# GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir +# GIT_WORK_TREE=$GL_ADMINDIR git commit -am start + +# Substitute $GL_ADMINDIR and $REPO_BASE appropriately. Note there is no +# space around the "=" in the second and third lines. + echo "cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir GIT_WORK_TREE=$GL_ADMINDIR git commit -am start " | ssh $user@$host +# MANUAL: now that the admin repo is created, you have to set the hooks +# properly. The install program does this. So cd back to the +# "gitolite-install" directory and run "src/install.pl" + ssh $user@$host "cd gitolite-install; src/install.pl" prompt "now we will clone the gitolite-admin repo to your workstation and see if it all hangs together. We'll do this in your \$HOME for now, and you can move it elsewhere later if you wish to." +# MANUAL: you're done! Log out of the server, come back to your workstation, +# and clone the admin repo using "git clone gitolite:gitolite-admin.git"! + cd $HOME git clone gitolite:gitolite-admin.git +# MANUAL: be sure to read the message below; this applies to you too... + echo echo echo ------------------------------------------------------------------------ From 0b81bfd6ec798adcd889d41087bdd1822f0f68d4 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Oct 2009 14:08:14 +0530 Subject: [PATCH 095/850] easy install: allow ports other than 22 for ssh to server --- src/00-easy-install.sh | 55 ++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 2ca8bf1..43e84a8 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -26,9 +26,10 @@ prompt() { } usage() { cat </dev/null || +ssh -p $port -o PasswordAuthentication=no $user@$host pwd >/dev/null || die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something" # MANUAL: make sure there's no "gitolite-admin" directory in $HOME (actually @@ -152,14 +159,16 @@ fi # The lines to be included look like this: # host gitolite -# hostname server # user git +# hostname server +# port 22 # identityfile ~/.ssh/sitaram echo " host gitolite - hostname $host user $user + hostname $host + port $port identityfile ~/.ssh/$admin_name" > $HOME/.ssh/.gl-stanza if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null @@ -191,8 +200,8 @@ rm $HOME/.ssh/.gl-stanza # server, to a directory called (for example) "gitolite-install". You may # have to create the directory first. -ssh $user@$host mkdir -p gitolite-install -rsync -a src conf doc $user@$host:gitolite-install/ +ssh -p $port $user@$host mkdir -p gitolite-install +rsync -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ # MANUAL: now log on to the server (ssh git@server) and get a command line. # This step is for your convenience; the script does it all from the client @@ -219,20 +228,20 @@ that all the paths etc. represent paths on the server!" ${VISUAL:-${EDITOR:-vi}} .gitolite.rc # copy the rc across -scp .gitolite.rc $user@$host: +scp -P $port .gitolite.rc $user@$host: prompt "ignore any 'please edit this file' or 'run this command' type lines in the next set of command outputs coming up. They're only relevant for a manual install, not this one..." # extract the GL_ADMINDIR and REPO_BASE locations -GL_ADMINDIR=$(ssh $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") -REPO_BASE=$( ssh $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") +GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") +REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") # MANUAL: still in the "gitolite-install" directory? Good. Run # "src/install.pl" -ssh $user@$host "cd gitolite-install; src/install.pl" +ssh -p $port $user@$host "cd gitolite-install; src/install.pl" # MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf # and add at least the following lines to it: @@ -252,13 +261,13 @@ repo testing " > gitolite.conf # send the config and the key to the remote -scp gitolite.conf $user@$host:$GL_ADMINDIR/conf/ +scp -P $port gitolite.conf $user@$host:$GL_ADMINDIR/conf/ -scp $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir +scp -P $port $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" -ssh $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf" +ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf" # ---------------------------------------------------------------------- # hey lets go the whole hog on this; setup push-to-admin! @@ -278,13 +287,13 @@ ssh $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf" echo "cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir GIT_WORK_TREE=$GL_ADMINDIR git commit -am start -" | ssh $user@$host +" | ssh -p $port $user@$host # MANUAL: now that the admin repo is created, you have to set the hooks # properly. The install program does this. So cd back to the # "gitolite-install" directory and run "src/install.pl" -ssh $user@$host "cd gitolite-install; src/install.pl" +ssh -p $port $user@$host "cd gitolite-install; src/install.pl" prompt "now we will clone the gitolite-admin repo to your workstation and see if it all hangs together. We'll do this in your \$HOME for now, From 48e18e1d2d67baaa8169d6a4c29de923ea47e863 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Oct 2009 09:53:30 +0530 Subject: [PATCH 096/850] easy install: some minor fixes - fix typo in introduction - detect if you're not running strictly as src/00-easy-install.sh --- src/00-easy-install.sh | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 43e84a8..7f21179 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -15,7 +15,7 @@ # command!) set -e -die() { echo "$@"; echo "run $0 again without any arguments for help and tips"; exit 1; } +die() { echo "$@"; echo; echo "run $0 again without any arguments for help and tips"; exit 1; } prompt() { echo echo @@ -47,7 +47,7 @@ Notes: Pre-requisites: - you must run this from the gitolite working tree top level directory. - This means you run this as "src/00-easy-install-clientside.sh" + This means you run this as "src/00-easy-install.sh" - you must already have pubkey based access to user@host. If you currently only have password access, use "ssh-copy-id" or something. Somehow get to the point where you can type "ssh user@host" and get a command line. Run @@ -62,6 +62,23 @@ EOFU exit 1; } +# ---------------------------------------------------------------------- +# basic sanity / argument checks +# ---------------------------------------------------------------------- + +# MANUAL: this *must* be run as "src/00-easy-install.sh", not by cd-ing to src +# and then running "./00-easy-install.sh" + +[[ $0 =~ ^src/00-easy-install.sh$ ]] || +{ + echo "please cd to the gitolite repo top level directory and run this as + 'src/00-easy-install.sh'" + exit 1; +} + +# MANUAL: (info) we'll use "git" as the user, "server" as the host, and +# "sitaram" as the admin_name in example commands shown below, if any + [[ -z $3 ]] && usage user=$1 host=$2 @@ -78,13 +95,6 @@ port=22 [[ "$user" =~ [^a-zA-Z0-9._-] ]] && die "user '$user' invalid" [[ "$admin_name" =~ [^a-zA-Z0-9._-] ]] && die "admin_name '$admin_name' invalid" -# MANUAL: (info) we'll use "git" as the user, "server" as the host, and -# "sitaram" as the admin_name in example commands shown below, if any - -# ---------------------------------------------------------------------- -# basic sanity checks -# ---------------------------------------------------------------------- - # MANUAL: make sure you're in the gitolite directory, at the top level. # The following files should all be visible: From 9e46920fe3544e6edb785595c3248e8d01f6ea5a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Oct 2009 20:02:38 +0530 Subject: [PATCH 097/850] faq: explain one user many keys a bit better --- doc/3-faq-tips-etc.mkd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index f24d30c..366c066 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -263,6 +263,11 @@ each of my pubkeys. In gitolite, we keep them separate: "sitaram@laptop.pub" and "sitaram@desktop.pub". The part before the "@" is the username, so gitolite knows these two keys belong to the same person. +Note that you don't say "sitaram@laptop" and so on in the **config** file -- +as far as the config file is concerned there's just **one** user called +"sitaram" -- so you only say "sitaram" there. Only the **pubkey files** have +the extra "@" stuff. + I think this is easier to maintain if you have to delete or change one of those keys. From fc36050972ce406e28024709e2deb98d32296675 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Oct 2009 20:39:34 +0530 Subject: [PATCH 098/850] easy install: one step toward idempotency... --- src/00-easy-install.sh | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 7f21179..aca409f 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -124,7 +124,6 @@ ssh -p $port -o PasswordAuthentication=no $user@$host pwd >/dev/null || # line). For example, "ssh-keygen -t rsa ~/.ssh/sitaram"; this would create # two files in ~/.ssh (sitaram and sitaram.pub) -[[ -f $HOME/.ssh/$admin_name.pub ]] && die "pubkey $HOME/.ssh/$admin_name.pub exists; can't proceed" prompt "the next command will create a new keypair for your gitolite access The pubkey will be $HOME/.ssh/$admin_name.pub. You will have to @@ -141,7 +140,13 @@ prompt "the next command will create a new keypair for your gitolite access This makes using passphrases very convenient." -ssh-keygen -t rsa -f $HOME/.ssh/$admin_name || die "ssh-keygen failed for some reason..." +if [[ -f $HOME/.ssh/$admin_name.pub ]] +then + prompt "Hmmm... pubkey $HOME/.ssh/$admin_name.pub exists; should I just re-use it? + Be sure you remember the passphrase, if you gave one when you created it!" +else + ssh-keygen -t rsa -f $HOME/.ssh/$admin_name || die "ssh-keygen failed for some reason..." +fi # MANUAL: copy the pubkey created to the server, say to /tmp. This would be # "scp ~/.ssh/sitaram.pub git@server:/tmp" (the script does this at a later @@ -185,7 +190,7 @@ if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null then prompt "your \$HOME/.ssh/config already has settings for gitolite. I will assume they're correct, but if they're not, please edit that file, delete - that paragraph (that line and the following few lines), and rerun. + that paragraph (that line and the following few lines), Ctrl-C, and rerun. In case you want to check right now (from another terminal) if they're correct, here's what they are *supposed* to look like: @@ -222,10 +227,6 @@ rsync -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ # change any paths. Make a note of the GL_ADMINDIR and REPO_BASE paths; you # will need them later -# give the user an opportunity to change the rc -cp conf/example.gitolite.rc .gitolite.rc - # hey here it means "release candidate" ;-) - prompt "the gitolite rc file needs to be edited by hand. The defaults are sensible, so if you wish, you can just exit the editor. @@ -235,6 +236,15 @@ understand what is what -- the rc file's documentation is inline. Please remember this file will actually be copied to the server, and that all the paths etc. represent paths on the server!" +# lets try and get the file from there first +if scp -P $port $user@$host:.gitolite.rc . +then + prompt "Oh hey... you already had a '.gitolite.rc' file on the server. I'll use + that instead of the default one..." +else + cp conf/example.gitolite.rc .gitolite.rc +fi + ${VISUAL:-${EDITOR:-vi}} .gitolite.rc # copy the rc across @@ -296,7 +306,7 @@ ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf" echo "cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir -GIT_WORK_TREE=$GL_ADMINDIR git commit -am start +GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty " | ssh -p $port $user@$host # MANUAL: now that the admin repo is created, you have to set the hooks From e0e9d389203730a0693fee9ff91b99bcec366982 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Oct 2009 21:21:29 +0530 Subject: [PATCH 099/850] easy install: minor formatting stuff --- src/00-easy-install.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index aca409f..ee5d943 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -20,7 +20,7 @@ prompt() { echo echo echo ------------------------------------------------------------------------ - echo "$1" + echo " $1" echo read -p '...press enter to continue or Ctrl-C to bail out' } @@ -228,13 +228,13 @@ rsync -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ # will need them later prompt "the gitolite rc file needs to be edited by hand. The defaults -are sensible, so if you wish, you can just exit the editor. + are sensible, so if you wish, you can just exit the editor. -Otherwise, make any changes you wish and save it. Read the comments to -understand what is what -- the rc file's documentation is inline. + Otherwise, make any changes you wish and save it. Read the comments to + understand what is what -- the rc file's documentation is inline. -Please remember this file will actually be copied to the server, and -that all the paths etc. represent paths on the server!" + Please remember this file will actually be copied to the server, and that + all the paths etc. represent paths on the server!" # lets try and get the file from there first if scp -P $port $user@$host:.gitolite.rc . @@ -251,8 +251,8 @@ ${VISUAL:-${EDITOR:-vi}} .gitolite.rc scp -P $port .gitolite.rc $user@$host: prompt "ignore any 'please edit this file' or 'run this command' type -lines in the next set of command outputs coming up. They're only -relevant for a manual install, not this one..." + lines in the next set of command outputs coming up. They're only relevant + for a manual install, not this one..." # extract the GL_ADMINDIR and REPO_BASE locations GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") @@ -316,8 +316,8 @@ GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty ssh -p $port $user@$host "cd gitolite-install; src/install.pl" prompt "now we will clone the gitolite-admin repo to your workstation -and see if it all hangs together. We'll do this in your \$HOME for now, -and you can move it elsewhere later if you wish to." + and see if it all hangs together. We'll do this in your \$HOME for now, + and you can move it elsewhere later if you wish to." # MANUAL: you're done! Log out of the server, come back to your workstation, # and clone the admin repo using "git clone gitolite:gitolite-admin.git"! From d125488107485d274c522946291e1cb3a9e6e05f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Oct 2009 10:02:58 +0530 Subject: [PATCH 100/850] doc/3 minor re-arrangement --- doc/3-faq-tips-etc.mkd | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 366c066..917590a 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -348,6 +348,26 @@ Just don't *show* the user this config file; it might sound insulting :-) ### design choices +#### keeping the parser and the access control separate + +There are two programs concerned with access control: + + * `gl-auth-command`, the program that is run via `~/.ssh/authorized_keys`; + this decides whether git should even be allowed to run (basic R/W/no + access). (This one cannot decide on the branch-level access; it is not + known at this point what branch is being accessed) + * the update-hook on each repo, which decides the per-branch permissions + +I have chosen to keep the relatively complex task of parsing the config file +out of them to keep them simpler (and faster). So any changes to the config +have to be first "compiled", and the access control programs use this +"compiled" version of the config. (The compile step also refreshes +`~/.ssh/authorized_keys`). + +If you choose the "easy install" method, all this is quite transparent to you +anyway. If you cannot use the easy install and must install manually, I have +clear instructions on how to set it up. + #### why we don't do "excludes" [umm... having said all this, I implemented it anyway; see the "rebel" @@ -398,23 +418,4 @@ understanding, and ***auditing***, what is allowed and what is not. And in security, "no confusion" is a good thing :-) -#### keeping the parser and the access control separate - -There are two programs concerned with access control: - - * `gl-auth-command`, the program that is run via `~/.ssh/authorized_keys`; - this decides whether git should even be allowed to run (basic R/W/no - access). (This one cannot decide on the branch-level access; it is not - known at this point what branch is being accessed) - * the update-hook on each repo, which decides the per-branch permissions - -I have chosen to keep the relatively complex task of parsing the config file -out of them to keep them simpler (and faster). So any changes to the config -have to be first "compiled", and the access control programs use this -"compiled" version of the config. (The compile step also refreshes -`~/.ssh/authorized_keys`). - -If you choose the "easy install" method, all this is quite transparent to you -anyway. If you cannot use the easy install and must install manually, I have -clear instructions on how to set it up. From 55ccb8291b36e7a03e3856d4c4c686815268c039 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Oct 2009 08:44:59 +0530 Subject: [PATCH 101/850] easy install: change ssh-agent detection use ssh-add -l instead of $SSH_AGENT_PID to decide if agent is running --- src/00-easy-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index ee5d943..fa148c7 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -157,7 +157,7 @@ fi # called SSH_AGENT_PID in your "env"), you should add this new key. The # command is "ssh-add ~/.ssh/sitaram" -if [[ -n $SSH_AGENT_PID ]] +if ssh-add -l &>/dev/null then prompt "you're running ssh-agent. We'll try and do an ssh-add of the private key we just created, otherwise this key won't get picked up. If From 030b3f29ef2f8f44d3a402e91315bc48c3461cb4 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Oct 2009 09:55:58 +0530 Subject: [PATCH 102/850] easy install: minor improvement in detection of password-less auth --- src/00-easy-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index fa148c7..c6201f1 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -110,7 +110,7 @@ ls src/gl-auth-command \ # is, running "ssh git@server" should log in straight away, without asking for # a password -ssh -p $port -o PasswordAuthentication=no $user@$host pwd >/dev/null || +ssh -p $port -o PasswordAuthentication=no $user@$host true || die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something" # MANUAL: make sure there's no "gitolite-admin" directory in $HOME (actually From 59e15e62a1e95cb635f35991d9884aa7ef9db08d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Oct 2009 10:02:45 +0530 Subject: [PATCH 103/850] support git installed outside default $PATH (also some minor fixes to doc/3) --- conf/example.gitolite.rc | 10 ++++++++++ doc/3-faq-tips-etc.mkd | 26 ++++++++++++++++++++++++-- src/00-easy-install.sh | 22 ++++++++++++++++++---- src/gl-auth-command | 5 ++++- src/gl-compile-conf | 5 ++++- src/install.pl | 5 ++++- 6 files changed, 64 insertions(+), 9 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 700fc0e..78df455 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -88,6 +88,16 @@ $PERSONAL=""; # NOTE: whatever value you choose, for security reasons it is better to make # it fully qualified -- that is, starting with "refs/" +# -------------------------------------- + +# if git on your server is on a standard path (that is +# ssh git@server git --version +# works), leave this setting as is. Otherwise, choose one of the +# alternatives, or write your own + +$GIT_PATH="" +# $GIT_PATH="/opt/bin/" + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 917590a..cda6ab0 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -14,6 +14,7 @@ In this document: * easier to specify gitweb/daemon access * better logging * one user, many keys + * support for git installed outside default PATH * who am I? * other cool things * "personal" branches @@ -226,7 +227,7 @@ bits and pieces. Here's an example, using short repo names for convenience: repo r2 # ...and so on... -### better logging +#### better logging If you have been too liberal with the permission to rewind, it has built-in logging as an emergency fallback if someone goes too far, or for audit @@ -271,6 +272,27 @@ the extra "@" stuff. I think this is easier to maintain if you have to delete or change one of those keys. +#### support for git installed outside default PATH + +The normal solution is to add to the system default PATH somehow, either by +munging `/etc/profile` or by enabling `PermitUserEnvironment` in +`/etc/ssh/sshd_config` and then setting the PATH in `~/.ssh/.environment`. +All these are security risks because they allow a lot more than just you and +your git install :-) + +And if you don't have root, you can't do this anyway. + +The only solution till now has been to ask every client to set the config +parameters `remote..receivepack` and `remote..uploadpack`. But +telling *every* client to do so is a pain... + +Gitolite lets you specify the directory in which git binaries are to be found, +via a new variable (`$GIT_PATH`) in the "rc" file. If this variable is +non-empty, it will be appended to the PATH environment variable before +attempting to run git stuff. + +Very easy, very simple, and completely transparent to the users :-) + #### who am I? As a developer, I send a file called `id_rsa.pub` to the gitolite admin. He @@ -292,7 +314,7 @@ In gitolite, it's simple: just ask nicely :-) ### other cool things -### "personal" branches +#### "personal" branches "personal" branches are great for corporate environments, where unauthenticated pull/clone is a no-no. Since a dev workstation cannot do diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index c6201f1..a9f5ecf 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -239,14 +239,28 @@ prompt "the gitolite rc file needs to be edited by hand. The defaults # lets try and get the file from there first if scp -P $port $user@$host:.gitolite.rc . then - prompt "Oh hey... you already had a '.gitolite.rc' file on the server. I'll use - that instead of the default one..." + prompt "Oh hey... you already had a '.gitolite.rc' file on the server. + Let's see if we can use that instead of the default one..." + sort < .gitolite.rc | perl -ne 'print "$1\n" if /^(\$\w+) *=/' > glrc.old + sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^(\$\w+) *=/' > glrc.new + if diff -u glrc.old glrc.new + then + prompt " looks like you're upgrading! I'm going to run your editor + with *both* the old and the new files (in that order), so you can add + in the lines pertaining to the variables shown with a '+' sign in the + above diff. This is necessary; please dont skip this + + [It's upto you to figure out how your editor handles 2 filename + arguments, switch between them, copy lines, etc ;-)]" + ${VISUAL:-${EDITOR:-vi}} .gitolite.rc conf/example.gitolite.rc + else + ${VISUAL:-${EDITOR:-vi}} .gitolite.rc + fi else cp conf/example.gitolite.rc .gitolite.rc + ${VISUAL:-${EDITOR:-vi}} .gitolite.rc fi -${VISUAL:-${EDITOR:-vi}} .gitolite.rc - # copy the rc across scp -P $port .gitolite.rc $user@$host: diff --git a/src/gl-auth-command b/src/gl-auth-command index 62573d4..8201403 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -24,13 +24,16 @@ use warnings; # ---------------------------------------------------------------------------- -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH); our %repos; my $glrc = $ENV{HOME} . "/.gitolite.rc"; die "parse $glrc failed: " . ($! or $@) unless do $glrc; die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; +# add a custom path for git binaries, if specified +$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; + # ---------------------------------------------------------------------------- # definitions specific to this program # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index ad50c4a..f51f97a 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -47,7 +47,7 @@ $Data::Dumper::Indent = 1; # common definitions # ---------------------------------------------------------------------------- -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH); # now that this thing *may* be run via "push to admin", any errors have to # grab the admin's ATTENTION so he won't miss them among the other messages a @@ -57,6 +57,9 @@ my $ATTN = "\n\t\t***** ERROR *****\n "; my $glrc = $ENV{HOME} . "/.gitolite.rc"; die "$ATTN parse $glrc failed: " . ($! or $@) unless do $glrc; +# add a custom path for git binaries, if specified +$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; + # ---------------------------------------------------------------------------- # definitions specific to this program # ---------------------------------------------------------------------------- diff --git a/src/install.pl b/src/install.pl index 3e1f655..f6e4142 100755 --- a/src/install.pl +++ b/src/install.pl @@ -3,7 +3,7 @@ use strict; use warnings; -our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF); +our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH); # wrapper around mkdir; it's not an error if the directory exists, but it is # an error if it doesn't exist and we can't create it @@ -33,6 +33,9 @@ unless (-f $glrc) { # ok now $glrc exists; read it to get the other paths die "parse $glrc failed: " . ($! or $@) unless do $glrc; +# add a custom path for git binaries, if specified +$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; + # mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); wrap_mkdir($repo_base_abs); From b3cab456d5882aa3f88af4fa33d756a7c4bdb547 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Oct 2009 10:16:23 +0530 Subject: [PATCH 104/850] easy-install: committed before testing? tsk tsk tsk! --- conf/example.gitolite.rc | 4 ++-- src/00-easy-install.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 78df455..b3b3361 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -95,8 +95,8 @@ $PERSONAL=""; # works), leave this setting as is. Otherwise, choose one of the # alternatives, or write your own -$GIT_PATH="" -# $GIT_PATH="/opt/bin/" +$GIT_PATH=""; +# $GIT_PATH="/opt/bin/"; # -------------------------------------- # per perl rules, this should be the last line in such a file: diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index a9f5ecf..a9d8700 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -245,6 +245,8 @@ then sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^(\$\w+) *=/' > glrc.new if diff -u glrc.old glrc.new then + ${VISUAL:-${EDITOR:-vi}} .gitolite.rc + else prompt " looks like you're upgrading! I'm going to run your editor with *both* the old and the new files (in that order), so you can add in the lines pertaining to the variables shown with a '+' sign in the @@ -253,8 +255,6 @@ then [It's upto you to figure out how your editor handles 2 filename arguments, switch between them, copy lines, etc ;-)]" ${VISUAL:-${EDITOR:-vi}} .gitolite.rc conf/example.gitolite.rc - else - ${VISUAL:-${EDITOR:-vi}} .gitolite.rc fi else cp conf/example.gitolite.rc .gitolite.rc From 481242f6cb973db3b45225b1e572d233f0e190a8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Oct 2009 11:46:04 +0530 Subject: [PATCH 105/850] doc/3: minor fix to an already minor change :) --- doc/3-faq-tips-etc.mkd | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index cda6ab0..3d97a3a 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -19,8 +19,8 @@ In this document: * other cool things * "personal" branches * design choices - * why we don't do "excludes" * keeping the parser and the access control separate + * why we don't do "excludes" ### common errors and mistakes @@ -439,5 +439,3 @@ The lack of overlap between refexes ensures ***no confusion*** in specifying, understanding, and ***auditing***, what is allowed and what is not. And in security, "no confusion" is a good thing :-) - - From 2a63026954955d78f20c361a6ef1d8faaef01746 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 14 Oct 2009 10:09:05 +0530 Subject: [PATCH 106/850] easy install: emphasise advice re passphrases on the new key --- src/00-easy-install.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index a9d8700..b47535b 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -126,15 +126,15 @@ ssh -p $port -o PasswordAuthentication=no $user@$host true || prompt "the next command will create a new keypair for your gitolite access - The pubkey will be $HOME/.ssh/$admin_name.pub. You will have to - choose a passphrase or hit enter for none. I recommend not having a - passphrase for now, and adding one with 'ssh-keygen -p' *as soon as* - all the setup is done and you've successfully cloned and pushed the - gitolite-admin repo. + The pubkey will be $HOME/.ssh/$admin_name.pub. You will have to choose a + passphrase or hit enter for none. I recommend not having a passphrase for + now, *especially* if you do not have a passphrase for the key which you + are already using to get server access! - After that, I suggest you (1) install 'keychain' or something - similar, and (2) add the following command to your bashrc (since - this is a non-default key) + Add one using 'ssh-keygen -p' after all the setup is done and you've + successfully cloned and pushed the gitolite-admin repo. After that, + install 'keychain' or something similar, and add the following command to + your bashrc (since this is a non-default key) ssh-add \$HOME/.ssh/$admin_name From 8e47e0117a5a824f4fe55822f53b45bb220d09df Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 14 Oct 2009 11:10:06 +0530 Subject: [PATCH 107/850] easy install: much more idempotent... - example config file is now all comments (should have been that way anyway) - we detect if it is an upgrade and act accordingly (see below) IMPORTANT: we assume that $admin_name remains the same in an upgrade -- that's how we detect it is an upgrade! Change that name or his pubkey, and you're toast! --- conf/example.conf | 53 ++++++++++++++++------------- src/00-easy-install.sh | 76 +++++++++++++++++++++++++++++------------- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 5e92b0a..81b6148 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -29,20 +29,23 @@ # we need lists at all? (1) to be able to reuse the same set of usernames in # the paras for different repos, (2) to keep the lines short, because lists # accumulate, like squid ACLs, so you can say: -@cust_A = cust1 cust2 -@cust_A = cust99 + +# @cust_A = cust1 cust2 +# @cust_A = cust99 + # and this is the same as listing all three on the same line # you can nest groups, but not recursively of course! -@interns = indy james -@staff = bob @interns -@staff = me alice -@secret_staff = bruce whitfield martin +# @interns = indy james +# @staff = bob @interns -@pubrepos = linux git +# @staff = me alice +# @secret_staff = bruce whitfield martin -@privrepos = supersecretrepo anothersecretrepo +# @pubrepos = linux git + +# @privrepos = supersecretrepo anothersecretrepo # ---------------------------------------------------------------------------- # REPOS, REFS, and PERMISSIONS @@ -80,29 +83,33 @@ # anyone can play in the sandbox, including making non-fastforward commits # (that's what the "+" means) -repo sandbox - RW+ = @all + +# repo sandbox +# RW+ = @all # my repo and alice's repo have the same memberships and access, so we just # put them both in the same stanza -repo myrepo alicerepo - RW+ = me alice - R = bob eve + +# repo myrepo alicerepo +# RW+ = me alice +# R = bob eve # this repo is visible to customers from company A but they can't write to it -repo cust_A_repo - R = @cust_A - RW = @staff + +# repo cust_A_repo +# R = @cust_A +# RW = @staff # idea for the tags syntax shamelessly copied from git.git # Documentation/howto/update-hook-example.txt :) -repo @privrepos thirdsecretrepo - RW+ pu = bruce - RW master next = bruce - RW refs/tags/v[0-9].* = bruce - RW refs/tags/ss/ = @secret_staff - RW tmp/.* = @secret_staff - R = @secret_staff + +# repo @privrepos thirdsecretrepo +# RW+ pu = bruce +# RW master next = bruce +# RW refs/tags/v[0-9].* = bruce +# RW refs/tags/ss/ = @secret_staff +# RW tmp/.* = @secret_staff +# R = @secret_staff # ---------------------------------------------------------------------------- # GITWEB AND DAEMON CONTROL diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index b47535b..66c0e6c 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -113,12 +113,6 @@ ls src/gl-auth-command \ ssh -p $port -o PasswordAuthentication=no $user@$host true || die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something" -# MANUAL: make sure there's no "gitolite-admin" directory in $HOME (actually -# for the manual flow this doesn't matter so much!) - -[[ -d $HOME/gitolite-admin ]] && - die "please delete or move aside the \$HOME/gitolite-admin directory" - # MANUAL: create a new key for you as a "gitolite user" (as opposed to you as # the "gitolite admin" who needs to login to the server and get a command # line). For example, "ssh-keygen -t rsa ~/.ssh/sitaram"; this would create @@ -241,20 +235,30 @@ if scp -P $port $user@$host:.gitolite.rc . then prompt "Oh hey... you already had a '.gitolite.rc' file on the server. Let's see if we can use that instead of the default one..." - sort < .gitolite.rc | perl -ne 'print "$1\n" if /^(\$\w+) *=/' > glrc.old - sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^(\$\w+) *=/' > glrc.new + sort < .gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > glrc.old + sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > glrc.new if diff -u glrc.old glrc.new then ${VISUAL:-${EDITOR:-vi}} .gitolite.rc else - prompt " looks like you're upgrading! I'm going to run your editor - with *both* the old and the new files (in that order), so you can add - in the lines pertaining to the variables shown with a '+' sign in the - above diff. This is necessary; please dont skip this + prompt " looks like you're upgrading, and there are some new rc + variables that this version is expecting that your old rc file doesn't + have. + + I'm going to run your editor with two filenames. The first is the + example file from this gitolite version. It will have a block (code + and comments) for each of the variables shown above with a '+' sign. + + The second is your current rc file, the destination. Copy those lines + into this file, preferably *with* the surrounding comments (for + clarity) and save it. + + This is necessary; please dont skip this! [It's upto you to figure out how your editor handles 2 filename arguments, switch between them, copy lines, etc ;-)]" - ${VISUAL:-${EDITOR:-vi}} .gitolite.rc conf/example.gitolite.rc + + ${VISUAL:-${EDITOR:-vi}} conf/example.gitolite.rc .gitolite.rc fi else cp conf/example.gitolite.rc .gitolite.rc @@ -277,6 +281,31 @@ REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$RE ssh -p $port $user@$host "cd gitolite-install; src/install.pl" +# MANUAL: if you're upgrading, just go to your clone of the admin repo, make a +# dummy change, and push. (This assumes that you didn't change the +# admin_name, pubkeys, userids, ports, or whatever, and you ran easy install +# only to upgrade the software). And then you are **done** -- ignore the rest +# of this file for the purposes of an upgrade + +# determine if this is an upgrade; we decide based on whether a pubkey called +# $admin_name.pub exists in $GL_ADMINDIR/keydir on the remote side +upgrade=0 +if ssh -p $port $user@$host cat $GL_ADMINDIR/keydir/$admin_name.pub &> /dev/null +then + prompt "this looks like an upgrade, based on the fact that a file called + $admin_name.pub already exists in $GL_ADMINDIR/keydir on the server. + + Please go to your clone of the admin repo, make a dummy change (like maybe + add a blank line to something), commit, and push. You're done! + + (This assumes that you didn't change the admin_name, pubkeys, userids, + ports, or whatever, and you ran easy install only to upgrade the + software)." + + exit 0 + +fi + # MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf # and add at least the following lines to it: @@ -284,7 +313,7 @@ ssh -p $port $user@$host "cd gitolite-install; src/install.pl" # RW+ = sitaram echo "#gitolite conf -#please see conf/example.conf for details on syntax and features +# please see conf/example.conf for details on syntax and features repo gitolite-admin RW+ = $admin_name @@ -296,20 +325,18 @@ repo testing # send the config and the key to the remote scp -P $port gitolite.conf $user@$host:$GL_ADMINDIR/conf/ - scp -P $port $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" - ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf" # ---------------------------------------------------------------------- # hey lets go the whole hog on this; setup push-to-admin! # ---------------------------------------------------------------------- -# MANUAL: make the first commit in the admin repo. This is a little more -# complex, so read carefully and substitute the correct paths. What you have -# to do is: +# MANUAL: you have to now make the first commit in the admin repo. This is +# a little more complex, so read carefully and substitute the correct paths. +# What you have to do is: # cd $REPO_BASE/gitolite-admin.git # GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir @@ -329,12 +356,13 @@ GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty ssh -p $port $user@$host "cd gitolite-install; src/install.pl" -prompt "now we will clone the gitolite-admin repo to your workstation - and see if it all hangs together. We'll do this in your \$HOME for now, - and you can move it elsewhere later if you wish to." - # MANUAL: you're done! Log out of the server, come back to your workstation, -# and clone the admin repo using "git clone gitolite:gitolite-admin.git"! +# and clone the admin repo using "git clone gitolite:gitolite-admin.git", or +# pull once again if you already have a clone + +prompt "now we will clone the gitolite-admin repo to your workstation +and see if it all hangs together. We'll do this in your \$HOME for now, +and you can move it elsewhere later if you wish to." cd $HOME git clone gitolite:gitolite-admin.git From 536e3198bf6a127a41aafdb79c5419da1d18b401 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 14 Oct 2009 14:09:34 +0530 Subject: [PATCH 108/850] doc fixes... - README: add a "what" section first, plus a few minor fixes - doc/5: - remove reference to obsolete ml branch URL; point it to the right place with the right section name - change text to reflect the fact that p-t-a is now the default! --- README.mkd | 33 ++++++++++++++++++++++++--------- doc/5-delegation.mkd | 39 ++++++++++++++++++++------------------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/README.mkd b/README.mkd index 7cbbaf3..81075cb 100644 --- a/README.mkd +++ b/README.mkd @@ -15,13 +15,28 @@ specifying who can and cannot *rewind* a given branch. In this document: + * what * why - * what's extra + * extra features * security * contact and license ---- +### what + +Gitolite allows a server to host many git repositories and provide access to +many developers, without having to give them real userids on the server. The +essential magic in doing this is ssh's pubkey access and the `authorized_keys` +file, and the inspiration was an older program called gitosis. + +Gitolite can restrict who can read from (clone/fetch) or write to (push) a +repository. It can also restrict who can push to what branch or tag, which is +very important in a corporate environment. Gitolite can be installed without +requiring root permissions, and with no additional software than git itself +and perl. It also has several other neat features described below and +elsewhere in the `doc/` directory. + ### why I have been using gitosis for a while, and have learnt a lot from it. But in @@ -33,15 +48,15 @@ a typical $DAYJOB setting, there are some issues: to help remotely, we never did manage to install it eventually) * you don't have root access, or the ability to add users (this is also true for people who have just one userid on a hosting provider) - * the most requested feature (see "what's new?") had to be written anyway + * the most requested feature (see below) had to be written anyway All of this pointed to a rewrite. In perl, naturally :-) -### what's extra +### extra features -**Per-branch permissions**. You will not believe how often I am asked this at -$DAYJOB. This is almost the single reason I started *thinking* about rolling -my own gitosis in the first place. +The most important feature I needed was **per-branch permissions**. This is +pretty much mandatory in a corporate environment, and is almost the single +reason I started *thinking* about rolling my own gitosis in the first place. It's not just "read-only" versus "read-write". Rewinding a branch (aka "non fast forward push") is potentially dangerous, but sometimes needed. So is @@ -53,8 +68,8 @@ Here're **some more features**. All of them are documented in detail somewhere in the `doc/` subdirectory. * simpler, yet far more powerful, config file syntax, including specifying - gitweb/daemon access. You'll need this power if you manage lots of users - + repos + combinations of access + gitweb/daemon access. You'll need this power if you manage lots of + users+repos+combinations of access * config file syntax gets checked upfront, and much more thoroughly * if your requirements are still too complex, you can split up the config file and delegate authority over parts of it @@ -62,7 +77,7 @@ somewhere in the `doc/` subdirectory. a synonym for "annotate" :-)] * "personal namespace" prefix for each dev * migration guide and simple converter for gitosis conf file - * "exclude" (or "deny" rights in the config file) -- this is the "rebel" + * "exclude" (or "deny") rights in the config file -- this is the "rebel" branch in the repository, and always will be ;-) ### security diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index 9924dc3..863268f 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -5,14 +5,14 @@ ### lots of repos, lots of users Gitolite tries to make it easy to manage access to lots of users and repos, -exploiting commonalities wherever possible. (The example under "simpler, more -powerful syntax" [here][ml] should give you an idea). As you can see, it lets -you specify bits and pieces of the access control separately -- i.e., *all* -the access specs for a certain repo need not be together; they can be +exploiting commonalities wherever possible. (The example in the section +"simpler syntax" in [this page][ml] should give you an idea). As you can see, +it lets you specify bits and pieces of the access control separately -- i.e., +*all* the access specs for a certain repo need not be together; they can be scattered, which makes it easier to manage the sort of slice and dice needed in that example. -[ml]: http://github.com/sitaramc/gitolite/blob/ml/update.mkd +[ml]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd But eventually the config file will become too big. If you let only one person have control, he could become a bottleneck. If you give it to multiple @@ -81,23 +81,24 @@ Splitting up the file does help, but there's also that little security issue -- anyone can make any change to any "fragment", unless you (once again) go back to Unix permissions to keep them separate. -Fixing that requires using "push-to-admin". +Fixing that requires using "push-to-admin", which is the default (and only) +method to make config changes in gitosis. -The page on [push-to-admin][ptd] explains clearly how to set it up. Unlike -gitosis, I refuse to make it the default because it's a support nightmare. -Don't get me wrong -- it's a great feature, and I use it myself, but the -learning curve is too steep to make it *required*. +For a long time, I refused to make "push to admin" the default because it's a +support nightmare. Don't get me wrong -- it's a great feature, and I use it +myself, but the learning curve was too steep to make it *required*. -[ptd]: http://github.com/sitaramc/gitolite/blob/pu/doc/4-push-to-admin.mkd +But eventually I figured out a way to front-load the darn ssh problem that +everyone on #git seemed to run up against, created a nice "easy install" +script, and made "push to admin" the default. -So, having setup push-to-admin, you add these lines to the main config file, -assuming Alice is in charge of all web browser development projects, Bob takes -care of web servers, and Mallory, as [tradition][abe] dictates, is in charge -of malware ;-) +So now, you just add these lines to the main config file, assuming Alice is in +charge of all web browser development projects, Bob takes care of web servers, +and Mallory, as [tradition][abe] dictates, is in charge of malware ;-) [abe]: http://en.wikipedia.org/wiki/Alice_and_Bob#List_of_characters - # you probably added these two lines while setting up push-to-admin + # these 2 lines were probably present already repo gitolite-admin RW+ = sitaram # now add these 3 lines @@ -111,10 +112,10 @@ of malware ;-) @webserver_repos = apache nginx @malware_repos = conficker storm -**As you can see, for each repo group you want to delegate authority over, -there's a *branch* in the `gitolite-admin` repo with the same name. If you +As you can see, **for each repo group** you want to delegate authority over, +there's a **branch with the same name** in the `gitolite-admin` repo. If you have write access to that branch, you are allowed to define rules for repos in -that repo group.** +that repo group. In other words, we use gitolite's per-branch permissions to "enforce" the separation between the delegated configs! From a91d5692913d9fd9412eb61424c3a904c2d99907 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 21 Oct 2009 19:19:00 +0530 Subject: [PATCH 109/850] ...because writing in crayon wasn't possible :) [long story...!] --- src/00-easy-install.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 66c0e6c..195da08 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -49,9 +49,11 @@ Pre-requisites: - you must run this from the gitolite working tree top level directory. This means you run this as "src/00-easy-install.sh" - you must already have pubkey based access to user@host. If you currently - only have password access, use "ssh-copy-id" or something. Somehow get to - the point where you can type "ssh user@host" and get a command line. Run - this program only after that is done + only have password access, use "ssh-copy-id" or something equivalent (or + copy the key manually). Somehow (doesn't matter how), get to the point + where you can type "ssh user@host" and get a command line. + + **DO NOT RUN THIS PROGRAM UNTIL THAT WORKS** Errors: - if you get a "pubkey [...filename...] exists" error, it is either leftover From d6dc1c0856e03cce1685bd7d40af212a9796b4c4 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 21 Oct 2009 19:19:20 +0530 Subject: [PATCH 110/850] added doc/6: more complex ssh setups --- doc/6-complex-ssh-setups.mkd | 135 +++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 doc/6-complex-ssh-setups.mkd diff --git a/doc/6-complex-ssh-setups.mkd b/doc/6-complex-ssh-setups.mkd new file mode 100644 index 0000000..c274a73 --- /dev/null +++ b/doc/6-complex-ssh-setups.mkd @@ -0,0 +1,135 @@ +# more complex ssh setups + +What do you need to know in order to create more complex ssh setups (for +instance if you have *two* gitolite servers you are administering)? Once more +unto the breach, here's more ssh magic! + +In this document: + + * files on client + * files on the server + * sanity checks + * two gitolite servers to manage? + +### files on client + + * default keypair; used to get shell access to servers. You would have + copied this pubkey to the gitolite server in order to log in without a + password. (On Linux systems you may have used `ssh-copy-id` to do that). + You would have done this *before* you ran the easy install script, because + otherwise easy install won't run! + + ~/.ssh/id_rsa + ~/.ssh/id_rsa.pub + + * gitolite keypair; the "sitaram" in this is the 3rd argument to the + `src/00-easy-install.sh` command you ran; the easy install script does the + rest + + ~/.ssh/sitaram + ~/.ssh/sitaram.pub + +### files on the server + + * the authkeys file; this contains one line containing the pubkey of each + user who is permitted to login without a password. + + Pubkey lines that give shell access look like this: + + ssh-rsa AAAAB3NzaC[snip]uPjrUiAUew== /home/sitaram/.ssh/id_rsa + + On a typical server there will be only one or two of these lines. + + Note that the last bit (`/home/sitaram/.ssh/id_rsa`) is purely a *comment* + field and can be anything. Also, the actual lines are much longer, about + 400 characters; I snipped 'em in the middle, as you can see. + + In contrast, pubkey lines that give access to git repos hosted by gitolite + looks like this: + + command="[some path]src/gl-auth-command sitaram",[some restrictions] ssh-rsa AAAAB3NzaC[snip]s18OnB42oQ== sitaram@sita-lt + + You will have many more of these lines -- one for every pubkey file in + `keydir/` of your gitolite-admin repo, with the corresponding username in + place of "sitaram" in the example above. + + The "command=" at the beginning ensures that when someone with the + corresponding private key logs in, they don't get a shell. Instead, the + `gl-auth-command` program is run, and (in this example) is given the + argument `sitaram`. This is how gitolite is invoked, (and is told the + user logging in is "sitaram"). + + * config file; this file has an entry for gitolite access: + + ~/.ssh/config + + Let's step back a bit. Normally, you might expect to access gitolite + repos like this: + + ssh://git@server/reponame.git + + But this won't work, because this ends up using the *default* keypair + (normally), which gives you a command line. Which means it won't invoke + the `gl-auth-command` program at all, and so none of gitolite's access + control will work. + + You need to force ssh to use the *other* keypair when performing a git + operation. With just ssh, that would be + + ssh -i ~/.ssh/sitaram git@server + + but git does not support putting an alternate keypair in the URL. + + Luckily, ssh has a very convenient way of capturing all the mundane + information (username, hostname, port number (if it's not the default 22), + and keypair to be used) in one "paragraph". This is what the para looks + like for us (the easy install script puts it there the first time): + + host gitolite + user git + hostname server + identityfile ~/.ssh/sitaram + + (The "gitolite" can be anything you want of course; it's like an alias for + all the stuff below it). This ensures that typing + + ssh gitolite + + is equivalent to + + ssh -i ~/.ssh/sitaram git@server + + and therefore this: + + git clone gitolite:reponame.git + + now works as expected, invoking the special keypair instead of the default + one. + +### sanity checks + + * `ssh gitolite` should get you the `SSH_ORIGINAL_COMMAND` error. If you + get a command line, something is wrong + + * conversely, `ssh git@server` should get you a command line + + * the "origin" URL in any clones should look like `gitolite:reponame.git` + instead of something more complex + +### two gitolite servers to manage? + + * they can have the same key; no harm there (example, sitaram.pub) + + * instead of just one ssh/config para, you now have two (assuming that the + remote user on both machines is called "git"): + + host gitolite + user git + hostname server + identityfile ~/.ssh/sitaram + + host gitolite2 + user git + hostname server2 + identityfile ~/.ssh/sitaram + From 96fa0da946cca5063f57902e2088126d3ae452f6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 23 Oct 2009 10:14:41 +0530 Subject: [PATCH 111/850] allow a/b/c type repos to be created --- doc/3-faq-tips-etc.mkd | 6 ------ src/gl-compile-conf | 3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 3d97a3a..43678e2 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -75,12 +75,6 @@ normal way, since it's not empty anymore. to hurt anything. This happens even in normal git, not just gitolite. [Update 2009-09-14; this has been fixed in git 1.6.4.3] - * if you specify a repo that is not at the top level `$REPO_BASE`, be sure - to manually create the intermediate directories first. For instance if - you specify a new repo called "a/b/c" to the config file and push, the - "compile" script will just `mkdir a/b/c.git`, assuming "a/b" has already - been created - * gitweb not able to read your repos? You can change the umask for newly created repos to something more relaxed -- see the `~/.gitolite.rc` file diff --git a/src/gl-compile-conf b/src/gl-compile-conf index f51f97a..d99976e 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -291,7 +291,8 @@ for my $repo (keys %repos) { unless (-d "$repo.git") { - mkdir("$repo.git") or die "$ATTN mkdir $repo.git failed: $!\n"; + system("mkdir", "-p", "$repo.git") and die "$ATTN mkdir $repo.git failed: $!\n"; + # erm, note that's "and die" not "or die" as is normal in perl wrap_chdir("$repo.git"); system("git --bare init"); system("cp $GL_ADMINDIR/src/update-hook.pl hooks/update"); From 8eefc036e023aea86ded1a355ccf7272cf7d01ba Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 23 Oct 2009 10:23:06 +0530 Subject: [PATCH 112/850] rc, pta-hook/doc: don't assume $HOME of 'git' user is /home/git (Thanks to Jerome Arbez-Gindre) --- conf/example.gitolite.rc | 2 +- doc/4-push-to-admin.mkd | 4 ++-- src/pta-hook.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index b3b3361..16bab4d 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -27,7 +27,7 @@ $REPO_UMASK = 0077; # gets you 'rwx------' # part of the setup of gitweb is a variable called $projects_list (please see # gitweb documentation for more on this). Set this to the same value: -$PROJECTS_LIST = "/home/git/projects.list"; +$PROJECTS_LIST = $ENV{HOME} . "/projects.list"; # -------------------------------------- diff --git a/doc/4-push-to-admin.mkd b/doc/4-push-to-admin.mkd index 8791480..7e5fc77 100644 --- a/doc/4-push-to-admin.mkd +++ b/doc/4-push-to-admin.mkd @@ -74,8 +74,8 @@ make the same changes below. repos they play havoc with my git commands, so this is how I do it) cd ~/repositories/gitolite-admin.git - GIT_WORK_TREE=/home/git/.gitolite git add conf/gitolite.conf keydir - GIT_WORK_TREE=/home/git/.gitolite git commit -am start + GIT_WORK_TREE=$HOME/.gitolite git add conf/gitolite.conf keydir + GIT_WORK_TREE=$HOME/.gitolite git commit -am start 4. Now we have to setup the post-update hook for push-to-admin to work. The hook should (1) make a forced checkout in the "live" config directory (which is diff --git a/src/pta-hook.sh b/src/pta-hook.sh index 5009313..2613340 100755 --- a/src/pta-hook.sh +++ b/src/pta-hook.sh @@ -2,7 +2,7 @@ # get this from your .gitolite.conf; and don't forget this is shell, while # that is perl :-) -export GL_ADMINDIR=/home/git/.gitolite +export GL_ADMINDIR=$HOME/.gitolite # checkout the master branch to $GL_ADMINDIR GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master From 78d02e143760a0df8022dae601d809a4d452cab3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Oct 2009 08:29:52 +0530 Subject: [PATCH 113/850] the rc file can now be in one of 2 places... Packaging gitolite for debian requires the rc file to be in /etc/gitolite. But non-root installs must still be supported, and they need it in $HOME. This means the rc file is no longer in a fixed place, which needs code to find the rc file first. See comments inside new file 'gitolite.pm' for details. The rest of the changes are in the other programs, to replace the hard-coded rc filename with a call to this new code. --- src/gitolite.pm | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/gl-auth-command | 11 +++++++++-- src/gl-compile-conf | 10 ++++++++-- src/install.pl | 16 ++++++++++------ src/update-hook.pl | 5 +++-- 5 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 src/gitolite.pm diff --git a/src/gitolite.pm b/src/gitolite.pm new file mode 100644 index 0000000..2c325e2 --- /dev/null +++ b/src/gitolite.pm @@ -0,0 +1,45 @@ +# this file is commonly used using "require". It is not required to use "use" +# (because it doesn't live in a different package) + +# warning: preceding para requires 4th attribute of a programmer after +# laziness, impatience, and hubris: sense of humour :-) + +# WARNING +# ------- +# the name of this file will change as soon as its function/feature set +# stabilises enough ;-) + +# right now all it does is define a function that tells you where to find the +# rc file + +# ---------------------------------------------------------------------------- +# where is the rc file hiding? +# ---------------------------------------------------------------------------- + +sub where_is_rc +{ + # till now, the rc file was in one fixed place: .gitolite.rc in $HOME of + # the user hosting the gitolite repos. This was fine, because gitolite is + # all about empowering non-root users :-) + + # then we wanted to make a debian package out of it (thank you, Rhonda!) + # which means (a) it's going to be installed by root anyway and (b) any + # config files have to be in /etc/ + + # the only way to resolve this in a backward compat way is to look for the + # $HOME one, and if you don't find it look for the /etc one + + # this common routine does that, setting an env var for the first one it + # finds + + return if $ENV{GL_RC}; + + for my $glrc ( $ENV{HOME} . "/.gitolite.rc", "/etc/gitolite/gitolite.rc" ) { + if (-f $glrc) { + $ENV{GL_RC} = $glrc; + return; + } + } +} + +1; diff --git a/src/gl-auth-command b/src/gl-auth-command index 8201403..62e5add 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -27,8 +27,15 @@ use warnings; our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH); our %repos; -my $glrc = $ENV{HOME} . "/.gitolite.rc"; -die "parse $glrc failed: " . ($! or $@) unless do $glrc; +# the common setup module is in the same directory as this running program is +my $bindir = $0; +$bindir =~ s/\/[^\/]+$//; +require "$bindir/gitolite.pm"; + +# ask where the rc file is, get it, and "do" it +&where_is_rc(); +die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; +# then "do" the compiled config file, whose name we now know die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; # add a custom path for git binaries, if specified diff --git a/src/gl-compile-conf b/src/gl-compile-conf index d99976e..5f8b3ae 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -54,8 +54,14 @@ our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UM # typical push generates my $ATTN = "\n\t\t***** ERROR *****\n "; -my $glrc = $ENV{HOME} . "/.gitolite.rc"; -die "$ATTN parse $glrc failed: " . ($! or $@) unless do $glrc; +# the common setup module is in the same directory as this running program is +my $bindir = $0; +$bindir =~ s/\/[^\/]+$//; +require "$bindir/gitolite.pm"; + +# ask where the rc file is, get it, and "do" it +&where_is_rc(); +die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; # add a custom path for git binaries, if specified $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; diff --git a/src/install.pl b/src/install.pl index f6e4142..1eb4a70 100755 --- a/src/install.pl +++ b/src/install.pl @@ -18,20 +18,24 @@ sub wrap_mkdir print STDERR "created $dir\n"; } -# the only path that is *fixed* (can't be changed without changing all 3 -# programs) is ~/.gitolite.rc +# the common setup module is in the same directory as this running program is +my $bindir = $0; +$bindir =~ s/\/[^\/]+$//; +require "$bindir/gitolite.pm"; -my $glrc = $ENV{HOME} . "/.gitolite.rc"; -unless (-f $glrc) { +# ask where the rc file is, get it, and "do" it +&where_is_rc(); +unless ($ENV{GL_RC}) { # doesn't exist. Copy it across, tell user to edit it and come back + my $glrc = $ENV{HOME} . "/.gitolite.rc"; system("cp conf/example.gitolite.rc $glrc"); print STDERR "created $glrc\n"; print STDERR "please edit it, change the paths if you wish to, and RERUN THIS SCRIPT\n"; exit; } -# ok now $glrc exists; read it to get the other paths -die "parse $glrc failed: " . ($! or $@) unless do $glrc; +# ok now the rc file exists; read it to get the other paths +die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; # add a custom path for git binaries, if specified $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; diff --git a/src/update-hook.pl b/src/update-hook.pl index 5f1c3bb..cdf7402 100755 --- a/src/update-hook.pl +++ b/src/update-hook.pl @@ -28,8 +28,9 @@ use warnings; our ($GL_CONF_COMPILED, $PERSONAL); our %repos; -my $glrc = $ENV{HOME} . "/.gitolite.rc"; -die "parse $glrc failed: " . ($! or $@) unless do $glrc; +# we should already have the GL_RC env var set when we enter this hook +die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; +# then "do" the compiled config file, whose name we now know die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; # ---------------------------------------------------------------------------- From 2f6ed42fcdf99bc4c2ab3c5757d3adf979b931fd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Oct 2009 17:33:06 +0530 Subject: [PATCH 114/850] install and compile: learnt a '-q' flag (not for manual use!) ...only for easy install to use in "quiet" mode --- src/gl-compile-conf | 13 ++++++++----- src/install.pl | 15 +++++++++------ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 5f8b3ae..cbdaf99 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -47,6 +47,9 @@ $Data::Dumper::Indent = 1; # common definitions # ---------------------------------------------------------------------------- +# setup quiet mode if asked; please do not use this when running manually +open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); + our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH); # now that this thing *may* be run via "push to admin", any errors have to @@ -343,12 +346,12 @@ for my $repo (sort keys %repos) { if ($repos{$repo}{'R'}{'daemon'}) { unless (-f $export_ok) { system("touch $export_ok"); - print STDERR "daemon add $repo.git\n"; + print "daemon add $repo.git\n"; } } else { if (-f $export_ok) { unlink($export_ok); - print STDERR "daemon del $repo.git\n"; + print "daemon del $repo.git\n"; } } } @@ -360,21 +363,21 @@ for my $repo (sort keys %repos) { # not in the old list; add it to the new one $projlist{"$repo.git"} = 1; $projlist_changed = 1; - print STDERR "gitweb add $repo.git\n"; + print "gitweb add $repo.git\n"; } } else { if ($projlist{"$repo.git"}) { # delete it from new list delete $projlist{"$repo.git"}; $projlist_changed = 1; - print STDERR "gitweb del $repo.git\n"; + print "gitweb del $repo.git\n"; } } } # has there been a change in the gitweb projects list? if ($projlist_changed) { - print STDERR "updating gitweb project list $PROJECTS_LIST\n"; + print "updating gitweb project list $PROJECTS_LIST\n"; my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); print $projlist_fh join("\n", sort keys %projlist), "\n" if %projlist; close $projlist_fh; diff --git a/src/install.pl b/src/install.pl index 1eb4a70..09a8345 100755 --- a/src/install.pl +++ b/src/install.pl @@ -5,17 +5,20 @@ use warnings; our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH); +# setup quiet mode if asked; please do not use this when running manually +open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); + # wrapper around mkdir; it's not an error if the directory exists, but it is # an error if it doesn't exist and we can't create it sub wrap_mkdir { my $dir = shift; if ( -d $dir ) { - print STDERR "$dir already exists\n"; + print "$dir already exists\n"; return; } mkdir($dir) or die "mkdir $dir failed: $!\n"; - print STDERR "created $dir\n"; + print "created $dir\n"; } # the common setup module is in the same directory as this running program is @@ -29,8 +32,8 @@ unless ($ENV{GL_RC}) { # doesn't exist. Copy it across, tell user to edit it and come back my $glrc = $ENV{HOME} . "/.gitolite.rc"; system("cp conf/example.gitolite.rc $glrc"); - print STDERR "created $glrc\n"; - print STDERR "please edit it, change the paths if you wish to, and RERUN THIS SCRIPT\n"; + print "created $glrc\n"; + print "please edit it, change the paths if you wish to, and RERUN THIS SCRIPT\n"; exit; } @@ -54,7 +57,7 @@ system("cp -R src doc $GL_ADMINDIR"); unless (-f $GL_CONF) { system("cp conf/example.conf $GL_CONF"); - print STDERR < Date: Sun, 25 Oct 2009 14:02:04 +0530 Subject: [PATCH 115/850] easy install: add "-q" option for experts; see usage message --- src/00-easy-install.sh | 88 ++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 195da08..ec76bc4 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -17,6 +17,14 @@ set -e die() { echo "$@"; echo; echo "run $0 again without any arguments for help and tips"; exit 1; } prompt() { + # receives two arguments. A short piece of text to be displayed, without + # pausing, in "quiet" mode, and a much longer one to be displayed, *with* + # a pause, in normal (verbose) mode + [[ $quiet == -q ]] && [[ -n $1 ]] && { + echo "$1" + return + } + shift echo echo echo ------------------------------------------------------------------------ @@ -26,7 +34,10 @@ prompt() { } usage() { cat </dev/null then - prompt "you're running ssh-agent. We'll try and do an ssh-add of the + prompt " ...adding key to agent..." \ + "you're running ssh-agent. We'll try and do an ssh-add of the private key we just created, otherwise this key won't get picked up. If you specified a passphrase in the previous step, you'll get asked for one now -- type in the same one." @@ -184,7 +197,8 @@ host gitolite if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null then - prompt "your \$HOME/.ssh/config already has settings for gitolite. I will + prompt "found gitolite para in ~/.ssh/config; assuming it is correct..." \ + "your \$HOME/.ssh/config already has settings for gitolite. I will assume they're correct, but if they're not, please edit that file, delete that paragraph (that line and the following few lines), Ctrl-C, and rerun. @@ -193,7 +207,8 @@ then $(cat ~/.ssh/.gl-stanza)" else - prompt "creating settings for your gitolite access in $HOME/.ssh/config; + prompt "creating gitolite para in ~/.ssh/config..." \ + "creating settings for your gitolite access in $HOME/.ssh/config; these are the lines that will be appended to your ~/.ssh/config: $(cat ~/.ssh/.gl-stanza)" @@ -212,7 +227,7 @@ rm $HOME/.ssh/.gl-stanza # have to create the directory first. ssh -p $port $user@$host mkdir -p gitolite-install -rsync -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ +rsync $quiet -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ # MANUAL: now log on to the server (ssh git@server) and get a command line. # This step is for your convenience; the script does it all from the client @@ -223,7 +238,8 @@ rsync -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ # change any paths. Make a note of the GL_ADMINDIR and REPO_BASE paths; you # will need them later -prompt "the gitolite rc file needs to be edited by hand. The defaults +prompt "finding/creating gitolite rc..." \ + "the gitolite rc file needs to be edited by hand. The defaults are sensible, so if you wish, you can just exit the editor. Otherwise, make any changes you wish and save it. Read the comments to @@ -233,19 +249,20 @@ prompt "the gitolite rc file needs to be edited by hand. The defaults all the paths etc. represent paths on the server!" # lets try and get the file from there first -if scp -P $port $user@$host:.gitolite.rc . +if scp -P $port $user@$host:.gitolite.rc . &>/dev/null then - prompt "Oh hey... you already had a '.gitolite.rc' file on the server. + prompt " ...trying to reuse existing rc" \ + "Oh hey... you already had a '.gitolite.rc' file on the server. Let's see if we can use that instead of the default one..." sort < .gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > glrc.old sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > glrc.new if diff -u glrc.old glrc.new then - ${VISUAL:-${EDITOR:-vi}} .gitolite.rc + [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} .gitolite.rc else - prompt " looks like you're upgrading, and there are some new rc - variables that this version is expecting that your old rc file doesn't - have. + prompt "" \ + " looks like you're upgrading, and there are some new rc variables + that this version is expecting that your old rc file doesn't have. I'm going to run your editor with two filenames. The first is the example file from this gitolite version. It will have a block (code @@ -264,13 +281,14 @@ then fi else cp conf/example.gitolite.rc .gitolite.rc - ${VISUAL:-${EDITOR:-vi}} .gitolite.rc + [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} .gitolite.rc fi # copy the rc across -scp -P $port .gitolite.rc $user@$host: +scp $quiet -P $port .gitolite.rc $user@$host: -prompt "ignore any 'please edit this file' or 'run this command' type +prompt "installing/upgrading..." \ + "ignore any 'please edit this file' or 'run this command' type lines in the next set of command outputs coming up. They're only relevant for a manual install, not this one..." @@ -281,7 +299,7 @@ REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$RE # MANUAL: still in the "gitolite-install" directory? Good. Run # "src/install.pl" -ssh -p $port $user@$host "cd gitolite-install; src/install.pl" +ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" # MANUAL: if you're upgrading, just go to your clone of the admin repo, make a # dummy change, and push. (This assumes that you didn't change the @@ -294,7 +312,8 @@ ssh -p $port $user@$host "cd gitolite-install; src/install.pl" upgrade=0 if ssh -p $port $user@$host cat $GL_ADMINDIR/keydir/$admin_name.pub &> /dev/null then - prompt "this looks like an upgrade, based on the fact that a file called + prompt "upgrade done!" \ + "this looks like an upgrade, based on the fact that a file called $admin_name.pub already exists in $GL_ADMINDIR/keydir on the server. Please go to your clone of the admin repo, make a dummy change (like maybe @@ -326,11 +345,11 @@ repo testing " > gitolite.conf # send the config and the key to the remote -scp -P $port gitolite.conf $user@$host:$GL_ADMINDIR/conf/ -scp -P $port $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir +scp $quiet -P $port gitolite.conf $user@$host:$GL_ADMINDIR/conf/ +scp $quiet -P $port $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" -ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf" +ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" # ---------------------------------------------------------------------- # hey lets go the whole hog on this; setup push-to-admin! @@ -356,15 +375,16 @@ GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty # properly. The install program does this. So cd back to the # "gitolite-install" directory and run "src/install.pl" -ssh -p $port $user@$host "cd gitolite-install; src/install.pl" +ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" # MANUAL: you're done! Log out of the server, come back to your workstation, # and clone the admin repo using "git clone gitolite:gitolite-admin.git", or # pull once again if you already have a clone -prompt "now we will clone the gitolite-admin repo to your workstation -and see if it all hangs together. We'll do this in your \$HOME for now, -and you can move it elsewhere later if you wish to." +prompt "cloning gitolite-admin repo..." \ +"now we will clone the gitolite-admin repo to your workstation + and see if it all hangs together. We'll do this in your \$HOME for now, + and you can move it elsewhere later if you wish to." cd $HOME git clone gitolite:gitolite-admin.git From 999513c1d9fcce63341b64310aae8e0ef98739cf Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Oct 2009 19:23:15 +0530 Subject: [PATCH 116/850] doc/install: document the new -q flag --- doc/0-INSTALL.mkd | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 5ef38cf..e6bfff5 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -9,6 +9,9 @@ slightly more manual process. Both are explained here. In this document: * easy install + * typical example run + * advantages over the older install methods + * disadvantages * manual install * other notes * next steps @@ -36,6 +39,17 @@ If so, just `cd` to that clone and run `src/00-easy-install.sh` and follow the prompts! (Running it without any arguments shows you usage plus other useful info). +#### typical example run + +A typical run for me is: + + src/00-easy-install.sh -q git my.git.server sitaram + +`-q` stands for "quiet" mode -- very minimal output, no verbose descriptions +of what it is going to do, and no pauses unless absolutely needed. However, +if you're doing this for the first time or you appreciate knowing what it is +actually doing, I suggest you skip the `-q`. + #### advantages over the older install methods * all ssh problems reduced to **just one pre-requisite**: enable ssh pubkey From 98124596ed387403a46c272d19b49620db715b38 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Oct 2009 21:32:32 +0530 Subject: [PATCH 117/850] doc/6: no idea how but this ended up with CRLF line endings (ugh!) --- doc/6-complex-ssh-setups.mkd | 270 +++++++++++++++++------------------ 1 file changed, 135 insertions(+), 135 deletions(-) diff --git a/doc/6-complex-ssh-setups.mkd b/doc/6-complex-ssh-setups.mkd index c274a73..fa69abf 100644 --- a/doc/6-complex-ssh-setups.mkd +++ b/doc/6-complex-ssh-setups.mkd @@ -1,135 +1,135 @@ -# more complex ssh setups - -What do you need to know in order to create more complex ssh setups (for -instance if you have *two* gitolite servers you are administering)? Once more -unto the breach, here's more ssh magic! - -In this document: - - * files on client - * files on the server - * sanity checks - * two gitolite servers to manage? - -### files on client - - * default keypair; used to get shell access to servers. You would have - copied this pubkey to the gitolite server in order to log in without a - password. (On Linux systems you may have used `ssh-copy-id` to do that). - You would have done this *before* you ran the easy install script, because - otherwise easy install won't run! - - ~/.ssh/id_rsa - ~/.ssh/id_rsa.pub - - * gitolite keypair; the "sitaram" in this is the 3rd argument to the - `src/00-easy-install.sh` command you ran; the easy install script does the - rest - - ~/.ssh/sitaram - ~/.ssh/sitaram.pub - -### files on the server - - * the authkeys file; this contains one line containing the pubkey of each - user who is permitted to login without a password. - - Pubkey lines that give shell access look like this: - - ssh-rsa AAAAB3NzaC[snip]uPjrUiAUew== /home/sitaram/.ssh/id_rsa - - On a typical server there will be only one or two of these lines. - - Note that the last bit (`/home/sitaram/.ssh/id_rsa`) is purely a *comment* - field and can be anything. Also, the actual lines are much longer, about - 400 characters; I snipped 'em in the middle, as you can see. - - In contrast, pubkey lines that give access to git repos hosted by gitolite - looks like this: - - command="[some path]src/gl-auth-command sitaram",[some restrictions] ssh-rsa AAAAB3NzaC[snip]s18OnB42oQ== sitaram@sita-lt - - You will have many more of these lines -- one for every pubkey file in - `keydir/` of your gitolite-admin repo, with the corresponding username in - place of "sitaram" in the example above. - - The "command=" at the beginning ensures that when someone with the - corresponding private key logs in, they don't get a shell. Instead, the - `gl-auth-command` program is run, and (in this example) is given the - argument `sitaram`. This is how gitolite is invoked, (and is told the - user logging in is "sitaram"). - - * config file; this file has an entry for gitolite access: - - ~/.ssh/config - - Let's step back a bit. Normally, you might expect to access gitolite - repos like this: - - ssh://git@server/reponame.git - - But this won't work, because this ends up using the *default* keypair - (normally), which gives you a command line. Which means it won't invoke - the `gl-auth-command` program at all, and so none of gitolite's access - control will work. - - You need to force ssh to use the *other* keypair when performing a git - operation. With just ssh, that would be - - ssh -i ~/.ssh/sitaram git@server - - but git does not support putting an alternate keypair in the URL. - - Luckily, ssh has a very convenient way of capturing all the mundane - information (username, hostname, port number (if it's not the default 22), - and keypair to be used) in one "paragraph". This is what the para looks - like for us (the easy install script puts it there the first time): - - host gitolite - user git - hostname server - identityfile ~/.ssh/sitaram - - (The "gitolite" can be anything you want of course; it's like an alias for - all the stuff below it). This ensures that typing - - ssh gitolite - - is equivalent to - - ssh -i ~/.ssh/sitaram git@server - - and therefore this: - - git clone gitolite:reponame.git - - now works as expected, invoking the special keypair instead of the default - one. - -### sanity checks - - * `ssh gitolite` should get you the `SSH_ORIGINAL_COMMAND` error. If you - get a command line, something is wrong - - * conversely, `ssh git@server` should get you a command line - - * the "origin" URL in any clones should look like `gitolite:reponame.git` - instead of something more complex - -### two gitolite servers to manage? - - * they can have the same key; no harm there (example, sitaram.pub) - - * instead of just one ssh/config para, you now have two (assuming that the - remote user on both machines is called "git"): - - host gitolite - user git - hostname server - identityfile ~/.ssh/sitaram - - host gitolite2 - user git - hostname server2 - identityfile ~/.ssh/sitaram - +# more complex ssh setups + +What do you need to know in order to create more complex ssh setups (for +instance if you have *two* gitolite servers you are administering)? Once more +unto the breach, here's more ssh magic! + +In this document: + + * files on client + * files on the server + * sanity checks + * two gitolite servers to manage? + +### files on client + + * default keypair; used to get shell access to servers. You would have + copied this pubkey to the gitolite server in order to log in without a + password. (On Linux systems you may have used `ssh-copy-id` to do that). + You would have done this *before* you ran the easy install script, because + otherwise easy install won't run! + + ~/.ssh/id_rsa + ~/.ssh/id_rsa.pub + + * gitolite keypair; the "sitaram" in this is the 3rd argument to the + `src/00-easy-install.sh` command you ran; the easy install script does the + rest + + ~/.ssh/sitaram + ~/.ssh/sitaram.pub + +### files on the server + + * the authkeys file; this contains one line containing the pubkey of each + user who is permitted to login without a password. + + Pubkey lines that give shell access look like this: + + ssh-rsa AAAAB3NzaC[snip]uPjrUiAUew== /home/sitaram/.ssh/id_rsa + + On a typical server there will be only one or two of these lines. + + Note that the last bit (`/home/sitaram/.ssh/id_rsa`) is purely a *comment* + field and can be anything. Also, the actual lines are much longer, about + 400 characters; I snipped 'em in the middle, as you can see. + + In contrast, pubkey lines that give access to git repos hosted by gitolite + looks like this: + + command="[some path]src/gl-auth-command sitaram",[some restrictions] ssh-rsa AAAAB3NzaC[snip]s18OnB42oQ== sitaram@sita-lt + + You will have many more of these lines -- one for every pubkey file in + `keydir/` of your gitolite-admin repo, with the corresponding username in + place of "sitaram" in the example above. + + The "command=" at the beginning ensures that when someone with the + corresponding private key logs in, they don't get a shell. Instead, the + `gl-auth-command` program is run, and (in this example) is given the + argument `sitaram`. This is how gitolite is invoked, (and is told the + user logging in is "sitaram"). + + * config file; this file has an entry for gitolite access: + + ~/.ssh/config + + Let's step back a bit. Normally, you might expect to access gitolite + repos like this: + + ssh://git@server/reponame.git + + But this won't work, because this ends up using the *default* keypair + (normally), which gives you a command line. Which means it won't invoke + the `gl-auth-command` program at all, and so none of gitolite's access + control will work. + + You need to force ssh to use the *other* keypair when performing a git + operation. With just ssh, that would be + + ssh -i ~/.ssh/sitaram git@server + + but git does not support putting an alternate keypair in the URL. + + Luckily, ssh has a very convenient way of capturing all the mundane + information (username, hostname, port number (if it's not the default 22), + and keypair to be used) in one "paragraph". This is what the para looks + like for us (the easy install script puts it there the first time): + + host gitolite + user git + hostname server + identityfile ~/.ssh/sitaram + + (The "gitolite" can be anything you want of course; it's like an alias for + all the stuff below it). This ensures that typing + + ssh gitolite + + is equivalent to + + ssh -i ~/.ssh/sitaram git@server + + and therefore this: + + git clone gitolite:reponame.git + + now works as expected, invoking the special keypair instead of the default + one. + +### sanity checks + + * `ssh gitolite` should get you the `SSH_ORIGINAL_COMMAND` error. If you + get a command line, something is wrong + + * conversely, `ssh git@server` should get you a command line + + * the "origin" URL in any clones should look like `gitolite:reponame.git` + instead of something more complex + +### two gitolite servers to manage? + + * they can have the same key; no harm there (example, sitaram.pub) + + * instead of just one ssh/config para, you now have two (assuming that the + remote user on both machines is called "git"): + + host gitolite + user git + hostname server + identityfile ~/.ssh/sitaram + + host gitolite2 + user git + hostname server2 + identityfile ~/.ssh/sitaram + From a23622a31bd2998fe61cc0fb61afbc6bb4885854 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 27 Oct 2009 09:47:06 +0530 Subject: [PATCH 118/850] doc/3: add section on unexpected gitwebauth good-ness! --- doc/3-faq-tips-etc.mkd | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 43678e2..3fced01 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -12,6 +12,7 @@ In this document: * error checking the config file * delegating parts of the config file * easier to specify gitweb/daemon access + * easier to link gitweb authorisation with gitolite * better logging * one user, many keys * support for git installed outside default PATH @@ -92,6 +93,8 @@ plain "git archive", because the Makefile adds a file called make master.tar # or maybe "make rebel.tar" or "make pu.tar" + + ### differences from gitosis Apart from the big ones listed in the top level README, and subjective ones @@ -191,6 +194,9 @@ for details. #### easier to specify gitweb/daemon access +Which of your repos should be accessible via plain HTTP or the `git://` +protocols (gitweb and git daemon, respectively)? + Specifying gitweb and/or daemon access for a repo is simple: give "read" permissions to two special usernames: `gitweb` and `daemon`. @@ -221,6 +227,61 @@ bits and pieces. Here's an example, using short repo names for convenience: repo r2 # ...and so on... + + +#### easier to link gitweb authorisation with gitolite + +Over and above whether a repo is even *shown* by gitweb, you may want to +further restrict people, allowing them to view *only* those repos for which +they have been given read access by gitolite. + +This requires that: + + * you have to have some sort of HTTP auth on your web server (out of my + scope, sorry!) + * the HTTP auth should use the same username (like "sitaram") as used in the + gitolite config (for the corresponding user) + +Once that is done, it's easy. Gitweb allows you to specify a subroutine to +decide on access. We use that feature and tie it to gitolite. Sample code +(untested, munged from something I saw [here][leho]) is given below. + +Note the **utter simplicity** of the actual check (just 1 line!). This is an +unexpected piece of luck coming from the decision to keep the config parse +separate from the actual access control. The config parser puts a pure perl +hash in that file named below as `$gl_conf_compiled`, so all the parsing is +already done and we just use it! + + # completely untested... but the basic idea should work fine + + # change these as needed + $repo_base = '/home/git/repositories/'; + $gl_conf_compiled = '/home/git/.gitolite/conf/gitolite.conf-compiled.pm'; + + # I assume this gives us the HTTP auth username + $username = $cgi->remote_user; + + # ---------- + + # parse the config file; updates %repos hash + our %repos; + die "parse $gl_conf_compiled failed: " . ($! or $@) unless do $gl_conf_compiled; + + # this is gitweb's mechanism; it calls whatever sub is pointed at by this + # variable to decide access yes/no + $export_auth_hook = sub { + my $reponame = shift; + # gitweb passes us the full repo path; so we strip the beginning... + $reponame =~ s/\Q$repo_base//; + # ...and the end, to get the repo name as it is specified in gitolite conf + $reponame =~ s/\.git$//; + + return exists $repos{$reponame}{R}{$username}; + } + + +[leho]: http://leho.kraav.com/news/2009/10/27/using-apache-authentication-with-gitweb-gitosis-repository-access-control/ + #### better logging If you have been too liberal with the permission to rewind, it has built-in From fd6fb9e9e1fe2ef5d74bb3d3d41730fc2555118d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 28 Oct 2009 10:57:38 +0530 Subject: [PATCH 119/850] easy install: save version info, print upgrading message --- src/00-easy-install.sh | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index ec76bc4..4b889ce 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -125,6 +125,25 @@ ls src/gl-auth-command \ ssh -p $port -o PasswordAuthentication=no $user@$host true || die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something" +# MANUAL: if needed, make a note of the version you are upgrading from, and to + +# record which version is being sent across; we assume it's HEAD +git describe --tags --long HEAD 2>/dev/null > src/VERSION || echo '(unknown)' > src/VERSION + +# what was the old version there? +export upgrade_details="you are upgrading from \ +$(ssh -p $port $user@$host cat gitolite-install/src/VERSION 2>/dev/null || echo '(unknown)' ) \ +to $(cat src/VERSION)" + +prompt "$upgrade_details" \ + "$upgrade_details + + Note: getting '(unknown)' for the 'from' version should only happen once. + Getting '(unknown)' for the 'to' version means you are probably installing + from a tar file dump, not a real clone. This is not an error but it's + nice to have those version numbers in case you need support. Try and + install from a clone" + # MANUAL: create a new key for you as a "gitolite user" (as opposed to you as # the "gitolite admin" who needs to login to the server and get a command # line). For example, "ssh-keygen -t rsa ~/.ssh/sitaram"; this would create @@ -167,7 +186,7 @@ fi if ssh-add -l &>/dev/null then - prompt " ...adding key to agent..." \ + prompt " ...adding key to agent..." \ "you're running ssh-agent. We'll try and do an ssh-add of the private key we just created, otherwise this key won't get picked up. If you specified a passphrase in the previous step, you'll get asked for one @@ -228,6 +247,7 @@ rm $HOME/.ssh/.gl-stanza ssh -p $port $user@$host mkdir -p gitolite-install rsync $quiet -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ +rm -f src/VERSION # MANUAL: now log on to the server (ssh git@server) and get a command line. # This step is for your convenience; the script does it all from the client @@ -312,7 +332,7 @@ ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" upgrade=0 if ssh -p $port $user@$host cat $GL_ADMINDIR/keydir/$admin_name.pub &> /dev/null then - prompt "upgrade done!" \ + prompt "done!" \ "this looks like an upgrade, based on the fact that a file called $admin_name.pub already exists in $GL_ADMINDIR/keydir on the server. From a19a7f01d74fae087b3e7882b47754d881446257 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 28 Oct 2009 13:33:24 +0530 Subject: [PATCH 120/850] auth, doc/3: print useful information when no command given --- doc/3-faq-tips-etc.mkd | 39 +++++++++++++++++++++++++-------------- src/gl-auth-command | 22 ++++++++++++++++------ 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 3fced01..0dc851a 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -16,7 +16,7 @@ In this document: * better logging * one user, many keys * support for git installed outside default PATH - * who am I? + * what repos do I have access to? * other cool things * "personal" branches * design choices @@ -348,24 +348,35 @@ attempting to run git stuff. Very easy, very simple, and completely transparent to the users :-) -#### who am I? + -As a developer, I send a file called `id_rsa.pub` to the gitolite admin. He -would rename it to "sitaram.pub" and put it in the key directory. Then he'd -add "sitaram" to the config file for the repos which I have access to. +#### what repos do I have access to? -But he could have called me "foobar" instead of "sitaram" -- as long as he -uses it consistently, it'll all work the same and look the same to me, because -the public key is all that matters. +Sometimes there are too many repos, maybe even named similarly, or with the +potential for typos, confusion about hyphens/underscores or upper/lower case, +etc. You'd just like a simple way to know what repos you have access to. -So do I have no reason to know what the admin named me? Well -- maybe (see -next section for one possible use). Anyway how do I find out? +Easy! Just use ssh and try to log in as if you were attempting to get a +shell: -In gitolite, it's simple: just ask nicely :-) - - $ ssh git@my.gitolite.server + $ ssh gitolite PTY allocation request failed on channel 0 - no SSH_ORIGINAL_COMMAND? I'm not a shell, sitaram! + hello sitaram, the gitolite version here is v0.6-17-g94ed189 + you have the following permissions: + R W Anu-WSD + R ROtest + R W SecureBrowse + R W entrans + R W git-notes + R W gitolite + R W gitolite-admin + R W indic_web_input + R W proxy + R W vkc + +Note that until this version, we used to put out an ugly `need +SSH_ORIGINAL_COMMAND` error, just like gitosis used to. All we did is put +that code path to better use :-) ### other cool things diff --git a/src/gl-auth-command b/src/gl-auth-command index 62e5add..4d9b805 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -24,7 +24,7 @@ use warnings; # ---------------------------------------------------------------------------- -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $GL_ADMINDIR); our %repos; # the common setup module is in the same directory as this running program is @@ -60,11 +60,21 @@ my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! # sanity checks on SSH_ORIGINAL_COMMAND # ---------------------------------------------------------------------------- -# SSH_ORIGINAL_COMMAND must exist. Since we also captured $user, we print -# that in the message so people saying "ssh git@server" can see which gitolite -# user he is being recognised as -my $cmd = $ENV{SSH_ORIGINAL_COMMAND} - or die "no SSH_ORIGINAL_COMMAND? I'm not a shell, $user!\n"; +# SSH_ORIGINAL_COMMAND must exist; if not, we die with a nice message +unless ($ENV{SSH_ORIGINAL_COMMAND}) { + # send back some useful info if no command was given + print "hello $user, the gitolite version here is "; + system("cat", "$GL_ADMINDIR/src/VERSION"); + print "\ryou have the following permissions:\n\r"; + for my $r (sort keys %repos) { + my $perm .= " R" if $repos{$r}{R}{$user}; + $perm .= " W" if $repos{$r}{W}{$user}; + print "$perm\t$r\n\r" if $perm; + } + exit 1; +} + +my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; # split into command and arguments; the pattern allows old style as well as # new style: "git-subcommand arg" or "git subcommand arg", just like gitosis From 071ff4c210bec491dced6e3865e236d520a2d8f9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 29 Oct 2009 22:12:29 +0530 Subject: [PATCH 121/850] easy install: cleaned up the closing credits; err I mean instructions :) --- src/00-easy-install.sh | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 4b889ce..333ad4f 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -332,7 +332,13 @@ ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" upgrade=0 if ssh -p $port $user@$host cat $GL_ADMINDIR/keydir/$admin_name.pub &> /dev/null then - prompt "done!" \ + prompt "done! + + If you forgot the help message you saw when you first ran this, there's a + somewhat generic version of it at the end of this file. Try: + + tail -30 $0 +" \ "this looks like an upgrade, based on the fact that a file called $admin_name.pub already exists in $GL_ADMINDIR/keydir on the server. @@ -414,19 +420,35 @@ git clone gitolite:gitolite-admin.git echo echo echo ------------------------------------------------------------------------ -echo "Cool -- we're done. Now you can edit the config file (currently -in ~/gitolite-admin/conf/gitolite.conf) to add more repos, users, etc. -When done, 'git add' the changed files, 'git commit' and 'git push'. +echo " +All done! -Read the comments in conf/example.conf for information about the config -file format -- like the rc file, this also has inline documentation. +The admin repo is currently cloned at ~/gitolite-admin; you can clone it +anywhere you like. To administer gitolite, make changes to the config file +(config/gitolite.conf) and/or the pubkeys (in subdirectory 'keydir') in any +clone, then git add, git commit, and git push. -Your URL for cloning any repo on this server will be +ADDING REPOS: Edit the config file to give *some* user access to the repo. +When you push, an empty repo will be created on the server, which authorised +users can then clone from, or push to. + +ADDING USERS: copy their pubkey as keydir/.pub, add it, commit and +push. + +CONFIG FILE FORMAT: see comments in conf/example.conf in the gitolite source. + +SSH MAGIC: Remember you (the admin) now have *two* keys to access the server +hosting your gitolite setup -- one to get you a command line, and one to get +you gitolite access; see doc/6-complex-ssh-setups.mkd. If you're not using +keychain or some such software, you may have to run this each time you log in: + + ssh-add ~/.ssh/$admin_name + +URLS: *Your* URL for cloning any repo on this server will be gitolite:reponame.git -However, any other users you set up will have to use +*Other* users you set up will have to use $user@$host:reponame.git - -unless they also create similar settings in their '.ssh/config' file." +" From 648dce20ec70bbad58bca2b4215780704dd87efa Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 29 Oct 2009 20:52:06 +0530 Subject: [PATCH 122/850] auth: make ".git" at the end optional --- src/gl-auth-command | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 4d9b805..f516657 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -84,9 +84,8 @@ my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; # git-receive-pack 'reponame.git' # including the single quotes -my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*).git'/); -die "bad command: $cmd. Make sure the repo name is exactly\n" . - "as in your config (no extra stuff before the name), plus a \".git\" at the end\n" +my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:.git)?'/); +die "bad command: $cmd. Make sure the repo name is exactly as in your config\n" unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ); From 26b4992162e83cfb6f5f8908886e7a3b3b482b97 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 23 Oct 2009 08:55:03 +0530 Subject: [PATCH 123/850] compile: (gh issue 2) apparently pubkeys don't always end in a newline I've never encountered this but it's an easy fix --- src/gl-compile-conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index cbdaf99..665d1d0 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -415,7 +415,10 @@ for my $pubkey (glob("*")) unless $user_list{$user}; $user_list{$user} = 'has pubkey'; print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; - print $newkeys_fh `cat $pubkey`; + # apparently some pubkeys don't end in a newline... + my $pubkey_content = `cat $pubkey`; + $pubkey_content =~ s/\s*$/\n/; + print $newkeys_fh $pubkey_content; } # lint check 3; a little more severe than the first two I guess... for my $user (sort keys %user_list) From b94ff910d11763b7514a9268250f30d90cce1354 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 30 Oct 2009 17:42:26 +0530 Subject: [PATCH 124/850] doc/6: explain that all this is *only* for the admin --- doc/6-complex-ssh-setups.mkd | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/6-complex-ssh-setups.mkd b/doc/6-complex-ssh-setups.mkd index fa69abf..f2292fa 100644 --- a/doc/6-complex-ssh-setups.mkd +++ b/doc/6-complex-ssh-setups.mkd @@ -11,6 +11,19 @@ In this document: * sanity checks * two gitolite servers to manage? +---- + +> But before we get to all that, let's clarify that all this is applicable +> **only** to the gitolite **admin**. He's the only one who needs both a +> shell and gitolite access, so he has **two** pubkeys in play. + +> Normal users have only one pubkey, since they are only allowed to access +> gitolite itself. They do not need to worry about any of this +> `~/.ssh/config` stuff, and their repo urls are very simple, like: +> `git@my.git.server:reponame.git`. + +---- + ### files on client * default keypair; used to get shell access to servers. You would have From 7907213316f85b5bb59b9f59b1f563d34f401dc2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 30 Oct 2009 17:43:26 +0530 Subject: [PATCH 125/850] easy install: clean up after yourself :) --- src/00-easy-install.sh | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 333ad4f..db097a7 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -15,6 +15,10 @@ # command!) set -e +export tmpgli=tmp-gl-install +trap "rm -rf $tmpgli" 0 +mkdir -p $tmpgli + die() { echo "$@"; echo; echo "run $0 again without any arguments for help and tips"; exit 1; } prompt() { # receives two arguments. A short piece of text to be displayed, without @@ -212,7 +216,7 @@ host gitolite user $user hostname $host port $port - identityfile ~/.ssh/$admin_name" > $HOME/.ssh/.gl-stanza + identityfile ~/.ssh/$admin_name" > $tmpgli/.gl-stanza if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null then @@ -223,19 +227,18 @@ then In case you want to check right now (from another terminal) if they're correct, here's what they are *supposed* to look like: -$(cat ~/.ssh/.gl-stanza)" +$(cat $tmpgli/.gl-stanza)" else prompt "creating gitolite para in ~/.ssh/config..." \ "creating settings for your gitolite access in $HOME/.ssh/config; these are the lines that will be appended to your ~/.ssh/config: -$(cat ~/.ssh/.gl-stanza)" +$(cat $tmpgli/.gl-stanza)" - cat $HOME/.ssh/.gl-stanza >> $HOME/.ssh/config + cat $tmpgli/.gl-stanza >> $HOME/.ssh/config # if the file didn't exist at all, it might have the wrong permissions chmod 644 $HOME/.ssh/config fi -rm $HOME/.ssh/.gl-stanza # ---------------------------------------------------------------------- # client side stuff almost done; server side now @@ -269,16 +272,16 @@ prompt "finding/creating gitolite rc..." \ all the paths etc. represent paths on the server!" # lets try and get the file from there first -if scp -P $port $user@$host:.gitolite.rc . &>/dev/null +if scp -P $port $user@$host:.gitolite.rc $tmpgli &>/dev/null then prompt " ...trying to reuse existing rc" \ "Oh hey... you already had a '.gitolite.rc' file on the server. Let's see if we can use that instead of the default one..." - sort < .gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > glrc.old - sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > glrc.new - if diff -u glrc.old glrc.new + sort < $tmpgli/.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.old + sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.new + if diff -u $tmpgli/glrc.old $tmpgli/glrc.new then - [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} .gitolite.rc + [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc else prompt "" \ " looks like you're upgrading, and there are some new rc variables @@ -297,15 +300,15 @@ then [It's upto you to figure out how your editor handles 2 filename arguments, switch between them, copy lines, etc ;-)]" - ${VISUAL:-${EDITOR:-vi}} conf/example.gitolite.rc .gitolite.rc + ${VISUAL:-${EDITOR:-vi}} conf/example.gitolite.rc $tmpgli/.gitolite.rc fi else - cp conf/example.gitolite.rc .gitolite.rc - [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} .gitolite.rc + cp conf/example.gitolite.rc $tmpgli/.gitolite.rc + [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc fi # copy the rc across -scp $quiet -P $port .gitolite.rc $user@$host: +scp $quiet -P $port $tmpgli/.gitolite.rc $user@$host: prompt "installing/upgrading..." \ "ignore any 'please edit this file' or 'run this command' type @@ -368,10 +371,10 @@ repo gitolite-admin repo testing RW+ = @all -" > gitolite.conf +" > $tmpgli/gitolite.conf # send the config and the key to the remote -scp $quiet -P $port gitolite.conf $user@$host:$GL_ADMINDIR/conf/ +scp $quiet -P $port $tmpgli/gitolite.conf $user@$host:$GL_ADMINDIR/conf/ scp $quiet -P $port $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" From 92d5062ad0ef5a1431ef3cdb3d78340f4269c77c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 30 Oct 2009 21:25:06 +0530 Subject: [PATCH 126/850] doc/src: major doc/help text revamp also removed some dead code from compile (pre PTA days) --- README.mkd | 20 ++++---- conf/example.gitolite.rc | 7 +-- doc/0-INSTALL.mkd | 37 +++++++------- doc/0-UPGRADE.mkd | 107 --------------------------------------- doc/1-migrate.mkd | 16 +++--- doc/2-admin.mkd | 13 +++-- doc/3-faq-tips-etc.mkd | 6 +++ doc/4-push-to-admin.mkd | 105 -------------------------------------- doc/5-delegation.mkd | 98 +++++++++-------------------------- src/00-easy-install.sh | 4 ++ src/gl-compile-conf | 17 ------- 11 files changed, 82 insertions(+), 348 deletions(-) delete mode 100644 doc/0-UPGRADE.mkd delete mode 100644 doc/4-push-to-admin.mkd diff --git a/README.mkd b/README.mkd index 81075cb..660e8f2 100644 --- a/README.mkd +++ b/README.mkd @@ -1,10 +1,8 @@ # gitolite -> [IMPORTANT: There is now an "upgrade" document in the "doc" directory; -> please read if upgrading gitolite] - -> [Update 2009-10-10: apart from all the nifty new features, there's now an -> "easy install" script in the src directory. Please see the INSTALL +> [Update 2009-10-28: apart from all the nifty new features, there's now an +> "easy install" script in the src directory. This script can be used to +> install as well as upgrade a gitolite install. Please see the INSTALL > document in the doc directory for details] ---- @@ -64,8 +62,10 @@ deleting a branch (which is really just an extreme form of rewind). I needed something in between allowing anyone to do it (the default) and disabling it completely (`receive.denyNonFastForwards` or `receive.denyDeletes`). -Here're **some more features**. All of them are documented in detail -somewhere in the `doc/` subdirectory. +Here're **some more features**. All of them, and more, are documented in +detail [here][gsdiff]. + +[gsdiff]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#diff * simpler, yet far more powerful, config file syntax, including specifying gitweb/daemon access. You'll need this power if you manage lots of @@ -73,6 +73,8 @@ somewhere in the `doc/` subdirectory. * config file syntax gets checked upfront, and much more thoroughly * if your requirements are still too complex, you can split up the config file and delegate authority over parts of it + * easier to specify gitweb/daemon access, and easier to link gitweb + authorisation with gitolite * more comprehensive logging [aka: management does not think "blame" is just a synonym for "annotate" :-)] * "personal namespace" prefix for each dev @@ -85,8 +87,8 @@ somewhere in the `doc/` subdirectory. Due to the environment in which this was created and the need it fills, I consider this a "security" program, albeit a very modest one. The code is very small and easily reviewable -- the 2 programs that actually control -access when a user logs in total about 200 lines of code (about -80 lines according to "sloccount"). +access when a user logs in total about 220 lines of code (about 90 lines +according to "sloccount"). For the first person to find a security hole in it, defined as allowing a normal user (not the gitolite admin) to read a repo, or write/rewind a ref, diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 16bab4d..4cda90b 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -57,12 +57,7 @@ $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m.log"; # -------------------------------------- -# Please DO NOT change the following paths unless you really know what you're -# doing. It'll work for now but it's officially deprecated to have them -# elsewhere from now on, and may break some future features. - -# Anyway, the conf files and keydirs don't grow constantly, (like the logs and -# the repositories do), so I don't think this is a major problem for anyone. +# Please DO NOT change these three paths $GL_CONF="$GL_ADMINDIR/conf/gitolite.conf"; $GL_KEYDIR="$GL_ADMINDIR/keydir"; diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index e6bfff5..c25a69e 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -1,9 +1,12 @@ # installing gitolite This document tells you how to install gitolite. After the install is done, -you may want to see the "admin" document for adding users, repos, etc. +you may want to see the [admin document][admin] for adding users, repos, etc. -There's an easy install script for Linux, and for other Unixes there's a +[admin]: http://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd + +There's an easy install script that requires bash (**strongly** recommended), +but if you have no bash or you're on one of the legacy Unixes there's a slightly more manual process. Both are explained here. In this document: @@ -25,12 +28,12 @@ case. **This script is meant to be run on your workstation, not on the server!** It will take care of all the server side work, *and* get you "push-to-admin" too :-) In short, it does **everything**! -Assumptions: +Assumptions/pre-requisites: * you have a server to host gitolite * git is installed on that server (and so is perl) * you have a userid on that server - * you have ssh-pubkey (password-less) login to that userid + * you have ssh-pubkey (**password-less**) login to that userid * (if you have only password access, run `ssh-keygen -t rsa` to create a new keypair if needed, then run `ssh-copy-id user@host`) * you have a clone or an archive of gitolite somewhere on your workstation @@ -64,26 +67,24 @@ actually doing, I suggest you skip the `-q`. #### disadvantages - * has been tested only with Linux + * need a recent bash ### manual install -If for some reason you cannot use the easy-install method, (for example, -you're on a non-Linux machine), it's not very complicated. Just open the file -`src/00-easy-install.sh` in a nice, syntax coloring, text editor, and follow -the instructions marked "MANUAL" :-) +If you don't have bash, it's not very complicated to do it manually. Just +open the file `src/00-easy-install.sh` in a nice, syntax coloring, text +editor, and follow the instructions marked "MANUAL" :-) ### other notes - * If you edit `~/.gitolite.rc` and change any paths, be sure to keep the - perl syntax -- you *don't* have to know perl to do so, it's fairly easy to - guess in this limited case - - * the config file is (by default) at `~/.gitolite/conf/gitolite.conf`, - though you can change its location in the "rc" file. Edit the file as you - wish. The comments in the example file (`conf/example.conf`) ought to be - clear enough but let me know if not + * if you run `src/00-easy-install.sh` without the `-q` option, you will be + given a chance to edit `~/.gitolite.rc`. You can change any options (such + as paths, for instance), but be sure to keep the perl syntax -- you + *don't* have to know perl to do so, it's fairly easy to guess in this + limited case. ### next steps -See the "admin" document for how to add users, etc. +The last message produced by the easy install script should tell you how to +add users, repos, etc., and you will find more details in the [admin][admin] +document. diff --git a/doc/0-UPGRADE.mkd b/doc/0-UPGRADE.mkd deleted file mode 100644 index 5e242f6..0000000 --- a/doc/0-UPGRADE.mkd +++ /dev/null @@ -1,107 +0,0 @@ -# upgrading gitolite atomically - -Upgrading is done **manually, on the server** (except the last step, which is -on your admin repo clone), even if you installed it using the easy install -script on the client. First, it's not as difficult as an install so you don't -really need a script. Second, you may have customised the "rc" file -(`~/.gitolite.rc` on the server) and I'm reluctant to mess with that in an -automated way. - -### general upgrade notes - -If you follow the steps below, you can make the upgrade "atomic", so you don't -have to do it at a "quiet" time or something. - -1. copy a tar file containing the new version to the server, untar it to some - temp directory and `cd` to it - -2. *prepare* the new version of `~/.gitolite.rc`. It **must** have **all** - the variables defined in `conf/example.gitolite.rc` (the "new" rc file), - because the new versions of the programs will be depending on seeing these - variables. - - However, it must also retain any customisations you made to the **old** - variables. - - So this is what you do: - - * make a copy of `conf/example.gitolite.rc` as `~/glrc.new` - * if your current `~/.gitolite.rc` had any customisations (where you - changed the defaults in some way), edit `~/glrc.new` and make those - same changes there - -3. upgrade the rc file first - - cp ~/glrc.new ~/.gitolite.rc - -4. upgrade the software - - src/install.pl - -5. compile the config once again, in case the *internal* format of the - compiled config file (`$GL_CONF_COMPILED`) has changed. - - To do this, you have to do a "git push" on the client side. That might - require a dummy change (maybe add a blank line somewhere) because - otherwise the push will not happen. - -And you're done. - -### upgrade notes for specific versions - -If any extra steps beyond the generic ones above are needed, they will be -listed here, newest first. - -#### upgrading from 410c9ba - -Between 410c9ba and this version, gitolite managed to make "push to admin" the -default for new installs, but in a much more painless way. If you're -upgrading, you're not forced to use "push to admin", but I'd suggest you: - - * make sure you have password-less (pubkey) login to a command line on your - server - * save your `~/.gitolite.rc`, `keydir/*.pub` and your `conf/gitolite.conf` - files from the server, bring them to your workstation - * then run `src/00-easy-install.sh` on the workstation, as if it were a - fresh install - * when the editor pops up to edit the rc file, delete all the lines in - it and copy them from the saved `~/.gitolite.rc` - * at the end of the script, after the gitolite-admin repo has been - cloned successfully, copy the saved `conf/gitolite.conf` and - `keydir/*.pub` to the clone, then add, commit, and push - -Gitolite also learnt to delegate parts of the config to other users. See -`doc/5-delegation.mkd` for details. - -#### upgrading from 8217ef9 - -Between 8217ef9 and this version, gitolite learnt to handle gitweb/daemon -access. As a result, the rc file acquired a new variable, `$PROJECTS_LIST`, -which you have to set to whatever your gitweb installation requires. - -#### upgrading from 86faae4 - -Between 86faae4 and this version, gitolite had a *major* change in the -*internal* format of the compiled config file. Please do not omit step 5 in -the generic instructions above. - -#### upgrading from 5758f69 - -Between 5758f69 and this version, gitolite learnt to allow "groupnames" for -repos as well. The `conf/example.conf` has been recommented to explain the -syntax but it's really a no-brainer: what you could previously do only for -usernames, you can now do for reponames also. - -#### upgrading from abb4580 - -Two new features (personal branches, and customisable logfile names/locations) -have been added between abb4580 and this version. - - * if you want to enable the personal branches feature, choose one of the - alternative values given for `$PERSONAL` or change it to something you - like; by default it is empty, which disables the feature - - * if you want the log files named or grouped differently, choose one of the - alternative values for `$GL_LOGT`. **Note** that if you choose to put - them in some other directory than the default, you **must** create that - directory (`mkdir`) yourself; gitolite will not do that for you diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd index fb0491f..aba1002 100644 --- a/doc/1-migrate.mkd +++ b/doc/1-migrate.mkd @@ -36,8 +36,11 @@ Here's how we migrated my work repos: Now, log off the server and get back to the client: -1. follow instructions to install gitolite; see install document. Make sure - that you **don't** change the default path for `$REPO_BASE`! +[inst]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd + +1. follow instructions to install gitolite; see the [install document][inst]. + Make sure that you **don't** change the default path for `$REPO_BASE` if + you edit the config file! 2. **convert** your gitosis config file. Substitute the path for your gitosis-admin clone in `$GSAC` below, and similarly the path for your @@ -51,10 +54,11 @@ Now, log off the server and get back to the client: cp $GSAC/keydir/* $GLAC/keydir -4. **Important: expand any multi-key files you may have**. See the "faq, - tips, etc" document in the doc directory for an explanation of what - multi-keys are, how gitosis does them and how gitolite does it - differently. +4. **Important: expand any multi-key files you may have**. [Here][mk]'s an + explanation of what multi-keys are, how gitosis does them and how gitolite + does it differently. + +[mk]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#multikeys You can split the keys manually, or use the following code (just copy-paste it into your xterm after "cd"-ing to your gitolite-admin repo diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 133455a..ed1d361 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -14,9 +14,9 @@ Please read on to see how to do this correctly. #### adding users and repos * ask each user who will get access to send you a public key. See other - sources (for example - [here](http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key)) - for how to do this + sources (for example [here][genpub]) for how to do this + +[genpub]: http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key * rename each public key according to the user's name, with a `.pub` extension, like `sitaram.pub` or `john-smith.pub`. You can also use @@ -40,8 +40,11 @@ so here goes. There's **no special syntax** for this -- just give read permission to a user called `gitweb` or `daemon`! (This also means you can't have a normal user -with either of those two names, but I doubt that's a problem!). See the "faq, -tips, etc" document for easy ways to specify access for multiple repositories. +with either of those two names, but I doubt that's a problem!). See the [faq, +tips, etc][ss] document for easy ways to specify access for multiple +repositories. + +[ss]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gwd Note that this does **not** install or configure gitweb/daemon -- that is a one-time setup you must do separately. All this does is: diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 0dc851a..64997b1 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -101,6 +101,8 @@ Apart from the big ones listed in the top level README, and subjective ones like "better config file format", there are some small, but significant and concrete, differences from gitosis. + + #### simpler syntax The basic syntax is simpler and cleaner but it goes beyond that: **you can @@ -192,6 +194,8 @@ access control for their own pieces. See [doc/5-delegation.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/5-delegation.mkd) for details. + + #### easier to specify gitweb/daemon access Which of your repos should be accessible via plain HTTP or the `git://` @@ -308,6 +312,8 @@ The other parts of the log line are the name of the repo, the refname being updated, the user updating it, and the refex pattern (from the config file) that matched, in case you need to debug the config file itself. + + #### one user, many keys I have a laptop and a desktop I need to access the server from. I have diff --git a/doc/4-push-to-admin.mkd b/doc/4-push-to-admin.mkd deleted file mode 100644 index 7e5fc77..0000000 --- a/doc/4-push-to-admin.mkd +++ /dev/null @@ -1,105 +0,0 @@ -# "push to admin" in gitolite - -**WARNING: THIS DOCUMENT IS OBSOLETE. DO NOT USE. IT IS RETAINED ONLY FOR -HISTORICAL PURPOSES**. Gitolite now does "push-to-admin" by default, and does -it very easily and simply by front-loading the ssh problem. See the install -doc for details. - ----- - -Gitosis's default mode of admin is by cloning and pushing the `gitosis-admin` -repo. I call this "push to admin". It's a very cool/cute feature, and I -loved it at first. - -But it's a ***support nightmare***. Half the gitosis angst on `#git` is -because of this feature. Gitolite does not use or endorse this method for -people new to git, or ssh or (worse) both. - -However, ***if*** you know git and ssh really, *really*, well and you know -what you're doing, this is a pretty nice thing to have -- does make life -easier, I admit. So, here is how to make PTA (hey nice acronym, just missing -an "I") work on gitolite as well. - -Note: - - * unlike the rest of gitolite, I can't help you with this unless you - convince me very quickly it's not a layer 8 problem :-) - - * here's a test to see if you should use this feature: after reading this - document, think about how you would switch back and forth between the - normal method and push-to-admin. If you can't immediately see what you - would need to do, please don't use it :-) - -The instructions are presented as shell commands; they should be fairly -obvious. All paths are from the default `~/.gitolite.rc`; if you changed any, -make the same changes below. - ----- - -> **WARNING**: the "compilation" runs via a `post-update` hook. Which, by -> definition, runs *after* the push has successfully completed. As a -> result, a *compilation error* will be visible to the admin doing the `git -> push` but will not otherwise look like an error to client-side git (in -> terms of return codes, scripting, etc., or even the "git gui" if you -> happen to use that for pushing). So be sure to watch out for compile -> error messages on push when you do this. - ----- - -#### server side setup - -1. First, on the server, log on to the `git` userid, add a new repo called - `gitolite-admin` to the config file, give yourself `RW` or `RW+` rights to it, - and "compile": - - cd ~/.gitolite - vim conf/gitolite.conf # add gitolite-admin repo, etc - src/gl-compile-conf - -2. Now, if you look at the "compile" script, it has an *automatic* local commit - inside, just for safety, which kicks in every time you compile. This only - works if it finds a ".git" directory, and it was designed as an "automatic - backup/safety net" type of thing, in case I accidentally deleted the whole - config file or something. - - We need to disable this, because now we have a *better* repo, one that is - manually pushed, and presumably has proper commit messages! - - mv .git .disable.git # yeah it's a hack, sue me - -3. Now the compile command created an empty, bare, "gitolite-admin" repo, so we - seed it with the current contents of the config and keys. (A note on the - `GIT_WORK_TREE` variable: I avoid setting these variables in the normal way - because I always forget to unset them later, and then when I `cd` to other - repos they play havoc with my git commands, so this is how I do it) - - cd ~/repositories/gitolite-admin.git - GIT_WORK_TREE=$HOME/.gitolite git add conf/gitolite.conf keydir - GIT_WORK_TREE=$HOME/.gitolite git commit -am start - -4. Now we have to setup the post-update hook for push-to-admin to work. The hook - should (1) make a forced checkout in the "live" config directory (which is - `~/.gitolite`), and (2) run the compile script. - - `src/pta-hook.sh` has the code you need; just copy it to the right place with - the right name, change the first line if needed, and make it executable: - - # (assuming pwd is still ~/repositories/gitolite-admin.git) - cp ~/.gitolite/src/pta-hook.sh hooks/post-update - - # if you changed $GL_ADMINDIR in ~/.gitolite.conf, then edit the hooks - # and change the first line: - vim hooks/post-update - - chmod +x hooks/post-update - ----- - -#### client side setup - -1. Now get to your workstation, and - - git clone git@server:gitolite-admin.git - - That's it, we're done. You're in gitosis land as far as this is concerned - now. So knock yourself out. Or lock yourself out... :-) diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index 863268f..af73b81 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -5,14 +5,13 @@ ### lots of repos, lots of users Gitolite tries to make it easy to manage access to lots of users and repos, -exploiting commonalities wherever possible. (The example in the section -"simpler syntax" in [this page][ml] should give you an idea). As you can see, -it lets you specify bits and pieces of the access control separately -- i.e., -*all* the access specs for a certain repo need not be together; they can be -scattered, which makes it easier to manage the sort of slice and dice needed -in that example. +exploiting commonalities wherever possible. (The example in [this +section][ss] should give you an idea). As you can see, it lets you specify +bits and pieces of the access control separately -- i.e., *all* the access +specs for a certain repo need not be together; they can be scattered, which +makes it easier to manage the sort of slice and dice needed in that example. -[ml]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd +[ss]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#simpler_syntax But eventually the config file will become too big. If you let only one person have control, he could become a bottleneck. If you give it to multiple @@ -30,14 +29,16 @@ Delegation offers a way to do all that. Note that delegated admins cannot create or remove users, not can they define new repos. They can only define access control rules for a set of repos they have been given authority for. -### splitting up the config file into fragments +---- It's easier to show how it all works with an example instead of long descriptions. +### splitting up the set of repos into groups + To start with, recall that gitolite allows you to specify **groups** (of users or repos, same syntax). So the basic idea is that the main config file -(`~/.gitolite/conf/gitolite.conf` by default) will specify some repo groups: +(`conf/gitolite.conf` in your admin repo clone) will specify some repo groups: # group your projects/repos however you want @webbrowser_repos = firefox lynx @@ -47,71 +48,25 @@ or repos, same syntax). So the basic idea is that the main config file # any other config as usual, including access control lines for any of the # above projects or groups -Now just create these files (assuming default `$GL_ADMINDIR` location): +### delegating ownership of groups of repos - ~/.gitolite/conf/fragments/webbrowser_repos.conf - ~/.gitolite/conf/fragments/webserver_repos.conf - ~/.gitolite/conf/fragments/malware_repos.conf - -Within each of those files put in whatever access control rules you want for -the repos that are members of that group. Notice that the basenames of the -files must be exactly the same as the name of the corresponding repo group in -the main config file. - -For instance, `~/.gitolite/conf/fragments/webbrowser_repos.conf` would only -contain access control for firefox and lynx. If it referenced any other repo -(say "storm") those lines would be ignored (and a warning message generated). - -When you run the compile script (`src/gl-compile-conf`), the **net effect is -as if you appended the contents of all the "fragment" files, in alphabetical -order, to the bottom of the main file**. - -(Except of course, while processing a fragment, it will ignore attempts to set -permissions for repos that are not members of the same-named "repo group"). - -And that's basically it, in the simplest usage. - -["But WAIT, there's MORE!"][bwtm] - -[bwtm]: http://en.wikipedia.org/wiki/Ed_Valenti#But_Wait.21_There.27s_More - -### delegating ownership of fragments - -Splitting up the file does help, but there's also that little security issue --- anyone can make any change to any "fragment", unless you (once again) go -back to Unix permissions to keep them separate. - -Fixing that requires using "push-to-admin", which is the default (and only) -method to make config changes in gitosis. - -For a long time, I refused to make "push to admin" the default because it's a -support nightmare. Don't get me wrong -- it's a great feature, and I use it -myself, but the learning curve was too steep to make it *required*. - -But eventually I figured out a way to front-load the darn ssh problem that -everyone on #git seemed to run up against, created a nice "easy install" -script, and made "push to admin" the default. - -So now, you just add these lines to the main config file, assuming Alice is in -charge of all web browser development projects, Bob takes care of web servers, -and Mallory, as [tradition][abe] dictates, is in charge of malware ;-) +Once the repos are grouped, give each person charge of one or more groups. +For example, Alice may be in charge of all web browser development projects, +Bob takes care of web servers, and Mallory, as [tradition][abe] dictates, is +in charge of malware ;-) [abe]: http://en.wikipedia.org/wiki/Alice_and_Bob#List_of_characters - # these 2 lines were probably present already +You do this by adding branches to the `gitolite-admin` repo: + + # the admin repo access was probably like this to start with: repo gitolite-admin RW+ = sitaram - # now add these 3 lines + # now add these lines to the config for the admin repo RW webbrowser_repos = alice RW webserver_repos = bob RW malware_repos = mallory - # you need these lines too -- they define what repos alice/bob/mallory are - # allowed to control - @webbrowser_repos = firefox lynx - @webserver_repos = apache nginx - @malware_repos = conficker storm - As you can see, **for each repo group** you want to delegate authority over, there's a **branch with the same name** in the `gitolite-admin` repo. If you have write access to that branch, you are allowed to define rules for repos in @@ -136,14 +91,7 @@ Here's how to use this in practice: * Alice then commits and pushes this branch to the `gitolite-admin` repo -Naturally, a successful push invokes the post-update hook that you installed -(while setting up [push-to-admin][ptd]). Here's what it does: - - * for each branch, say `br`, of the `gitolite-admin` repo, it checks if - there is a file called `conf/fragments/br.conf` - - * if there is, it extracts it and copies it with the exact same name and - path, into the `$GL_ADMINDIR` directory (`~/.gitolite` by default) - -After that, it runs the compile script, and things work the same as described -in the previous section. +Naturally, a successful push invokes the post-update hook that the admin repo +has, which eventually runs the compile script. The **net effect** is as if +you appended the contents of all the "fragment" files, in alphabetical order, +to the bottom of the main file. diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index db097a7..b8ad9fe 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -283,6 +283,10 @@ then then [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc else + # MANUAL: if you're upgrading, read the instructions below and + # manually make sure your final ~/.gitolite.rc has both your existing + # customisations as well as any new variables that the new version of + # gitolite has introduced prompt "" \ " looks like you're upgrading, and there are some new rc variables that this version is expecting that your old rc file doesn't have. diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 665d1d0..629bded 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -434,20 +434,3 @@ close $newkeys_fh or die "$ATTN close newkeys failed: $!\n"; system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys"); system("rm $ENV{HOME}/.ssh/new_authkeys"); - -# if the gl admin directory (~/.gitolite) is itself a git repo, do an -# autocheckin. nothing fancy; this is a "just in case" type of thing. -wrap_chdir($GL_ADMINDIR); -if (-d ".git") -{ - system("git add -A conf keydir"); # stage all operational data - # and if there are any - if (system("git diff --cached --quiet") ) - { - open my $commit_ph, "|-", "git commit -F -" - or die "$ATTN open commit failed: $!\n"; - print $commit_ph "keydir changed\n\n"; - print $commit_ph `git diff --cached --name-status`; - close $commit_ph or die "$ATTN close commit failed: $!\n"; - } -} From 33305ed8e7cdc3527ecfe4b681ace165885a2cdc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 5 Nov 2009 13:59:36 +0530 Subject: [PATCH 127/850] doc/1: fix formatting problem on github (local mkd worked fine... weird!) --- doc/1-migrate.mkd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd index aba1002..fb5de34 100644 --- a/doc/1-migrate.mkd +++ b/doc/1-migrate.mkd @@ -58,8 +58,6 @@ Now, log off the server and get back to the client: explanation of what multi-keys are, how gitosis does them and how gitolite does it differently. -[mk]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#multikeys - You can split the keys manually, or use the following code (just copy-paste it into your xterm after "cd"-ing to your gitolite-admin repo clone): @@ -84,3 +82,6 @@ Now, log off the server and get back to the client: the files to make sure this worked properly* 5. Check all your changes to your gitolite-admin clone, commit, and push + +[mk]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#multikeys + From 8aecaa2da24142c2047b397272bde038828238aa Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 3 Nov 2009 14:01:03 +0530 Subject: [PATCH 128/850] doc/6: rename the file, change focus completely --- ...h-setups.mkd => 6-ssh-troubleshooting.mkd} | 133 +++++++++++++----- src/00-easy-install.sh | 2 +- 2 files changed, 95 insertions(+), 40 deletions(-) rename doc/{6-complex-ssh-setups.mkd => 6-ssh-troubleshooting.mkd} (57%) diff --git a/doc/6-complex-ssh-setups.mkd b/doc/6-ssh-troubleshooting.mkd similarity index 57% rename from doc/6-complex-ssh-setups.mkd rename to doc/6-ssh-troubleshooting.mkd index f2292fa..efd5f2b 100644 --- a/doc/6-complex-ssh-setups.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -1,15 +1,17 @@ -# more complex ssh setups +# ssh troubleshooting -What do you need to know in order to create more complex ssh setups (for -instance if you have *two* gitolite servers you are administering)? Once more -unto the breach, here's more ssh magic! +Ssh has always been the biggest troublespot in all this. While gitolite makes +it as easy as possible, you might still run into trouble sometimes. In this document: - * files on client - * files on the server - * sanity checks - * two gitolite servers to manage? + * ssh sanity checks + * explanation + * files on the server + * files on client + * more complex ssh setups + * two gitolite servers to manage? + * further reading ---- @@ -18,31 +20,61 @@ In this document: > shell and gitolite access, so he has **two** pubkeys in play. > Normal users have only one pubkey, since they are only allowed to access -> gitolite itself. They do not need to worry about any of this -> `~/.ssh/config` stuff, and their repo urls are very simple, like: -> `git@my.git.server:reponame.git`. +> gitolite itself. They do not need to worry about any of this stuff, and +> their repo urls are very simple, like: `git@my.git.server:reponame.git`. ---- -### files on client +### ssh sanity checks - * default keypair; used to get shell access to servers. You would have - copied this pubkey to the gitolite server in order to log in without a - password. (On Linux systems you may have used `ssh-copy-id` to do that). - You would have done this *before* you ran the easy install script, because - otherwise easy install won't run! +There are two quick sanity checks you can run: - ~/.ssh/id_rsa - ~/.ssh/id_rsa.pub + * running `ssh gitolite` should get you a list of repos you have rights to + access, as described [here][myrights] - * gitolite keypair; the "sitaram" in this is the 3rd argument to the - `src/00-easy-install.sh` command you ran; the easy install script does the - rest +[myrights]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#myrights - ~/.ssh/sitaram - ~/.ssh/sitaram.pub + * conversely, `ssh git@server` should get you a command line -### files on the server +If one or both of these does not work as expected, do this: + + * first, check that your `~/.ssh` has two public keys, like below: + + $ ls -al ~/.ssh/*.pub + -rw-r--r-- 1 sitaram sitaram 409 2008-04-21 17:42 /home/sitaram/.ssh/id_rsa.pub + -rw-r--r-- 1 sitaram sitaram 409 2009-10-15 16:25 /home/sitaram/.ssh/sitaram.pub + + If it doesn't you have either lost your keys or you're on the wrong + machine. As long as you have password access to the server you can alweys + recover; just pretend you're installing from scratch and start over. + + * next, try running `ssh-add -l`. On my desktop the output looks like this: + + 2048 63:ea:ab:10:d2:4f:88:f4:85:cb:d3:7d:3a:83:37:9a /home/sitaram/.ssh/id_rsa (RSA) + 2048 d7:23:89:12:5f:22:4f:ad:54:7d:7e:f8:f5:2a:e9:13 /home/sitaram/.ssh/sitaram (RSA) + + If you get only one line (typically the top one), you should ssh-add the + other one, using (in my case) `ssh-add ~/.ssh/sitaram`. + + If you get no output, add both of them and check `ssh-add -l` again. + + If this error keeps happening please consider installing [keychain][kch] + or something similar, or add these commands to your bash startup scripts. + +[kch]: http://www.gentoo.org/proj/en/keychain/ + + * Finally, make sure your `~/.ssh/config` has the required `host gitolite` + para (see below for more on this). + +Once these sanity checks have passed, things should be fine. However, if you +still have problems, make sure that the "origin" URL in any clones looks like +`gitolite:reponame.git`, not `git@server:reponame.git`. + +### explanation + +Here's how it all hangs together. + +#### files on the server * the authkeys file; this contains one line containing the pubkey of each user who is permitted to login without a password. @@ -72,12 +104,30 @@ In this document: argument `sitaram`. This is how gitolite is invoked, (and is told the user logging in is "sitaram"). +#### files on client + + * default keypair; used to get shell access to servers. You would have + copied this pubkey to the gitolite server in order to log in without a + password. (On Linux systems you may have used `ssh-copy-id` to do that). + You would have done this *before* you ran the easy install script, because + otherwise easy install won't run! + + ~/.ssh/id_rsa + ~/.ssh/id_rsa.pub + + * gitolite keypair; the "sitaram" in this is the 3rd argument to the + `src/00-easy-install.sh` command you ran; the easy install script does the + rest + + ~/.ssh/sitaram + ~/.ssh/sitaram.pub + * config file; this file has an entry for gitolite access: ~/.ssh/config - Let's step back a bit. Normally, you might expect to access gitolite - repos like this: + To understand why we need that, let's step back a bit. Normally, you + might expect to access gitolite repos like this: ssh://git@server/reponame.git @@ -87,7 +137,7 @@ In this document: control will work. You need to force ssh to use the *other* keypair when performing a git - operation. With just ssh, that would be + operation. With normal ssh, that would be ssh -i ~/.ssh/sitaram git@server @@ -103,8 +153,8 @@ In this document: hostname server identityfile ~/.ssh/sitaram - (The "gitolite" can be anything you want of course; it's like an alias for - all the stuff below it). This ensures that typing + (The "gitolite" can be anything you want of course; it's like a group name + for all the stuff below it). This ensures that typing ssh gitolite @@ -119,17 +169,12 @@ In this document: now works as expected, invoking the special keypair instead of the default one. -### sanity checks +### more complex ssh setups - * `ssh gitolite` should get you the `SSH_ORIGINAL_COMMAND` error. If you - get a command line, something is wrong +What do you need to know in order to create more complex ssh setups (for +instance if you have *two* gitolite servers you are administering)? - * conversely, `ssh git@server` should get you a command line - - * the "origin" URL in any clones should look like `gitolite:reponame.git` - instead of something more complex - -### two gitolite servers to manage? +#### two gitolite servers to manage? * they can have the same key; no harm there (example, sitaram.pub) @@ -146,3 +191,13 @@ In this document: hostname server2 identityfile ~/.ssh/sitaram + * now access one server's repos as `gitolite:reponame.git` and the other + server's repos as `gitolite2:reponame.git`. + +### further reading + +While this focused mostly on the client side ssh, you may also want to read +[this][glb] for a much more detailed explanation of the ssh magic on the +server side. + +[glb]: http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index b8ad9fe..4cc6ff1 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -446,7 +446,7 @@ CONFIG FILE FORMAT: see comments in conf/example.conf in the gitolite source. SSH MAGIC: Remember you (the admin) now have *two* keys to access the server hosting your gitolite setup -- one to get you a command line, and one to get -you gitolite access; see doc/6-complex-ssh-setups.mkd. If you're not using +you gitolite access; see doc/6-ssh-troubleshooting.mkd. If you're not using keychain or some such software, you may have to run this each time you log in: ssh-add ~/.ssh/$admin_name From 31fd24a76c77bc9e9a927c7244d5ca27753dc06e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 3 Nov 2009 20:24:53 +0530 Subject: [PATCH 129/850] compile: death should be a little louder and clearer :) --- src/gl-compile-conf | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 629bded..3d8177b 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -55,7 +55,8 @@ our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UM # now that this thing *may* be run via "push to admin", any errors have to # grab the admin's ATTENTION so he won't miss them among the other messages a # typical push generates -my $ATTN = "\n\t\t***** ERROR *****\n "; +my $ABRT = "\n\t\t***** ABORTING *****\n "; +my $WARN = "\n\t\t***** WARNING *****\n "; # the common setup module is in the same directory as this running program is my $bindir = $0; @@ -64,7 +65,7 @@ require "$bindir/gitolite.pm"; # ask where the rc file is, get it, and "do" it &where_is_rc(); -die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; +die "$ABRT parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; # add a custom path for git binaries, if specified $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; @@ -113,11 +114,11 @@ umask($REPO_UMASK); # ---------------------------------------------------------------------------- sub wrap_chdir { - chdir($_[0]) or die "$ATTN chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; + chdir($_[0]) or die "$ABRT chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; } sub wrap_open { - open (my $fh, $_[0], $_[1]) or die "$ATTN open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n" . + open (my $fh, $_[0], $_[1]) or die "$ABRT open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n" . ( $_[2] || '' ); # suffix custom error message if given return $fh; } @@ -131,10 +132,10 @@ sub expand_list { # we test with the slightly more relaxed pattern here; we'll catch the # "/" in user name thing later; it doesn't affect security anyway - die "$ATTN bad user or repo name $item\n" unless $item =~ $REPONAME_PATT; + die "$ABRT bad user or repo name $item\n" unless $item =~ $REPONAME_PATT; if ($item =~ /^@/) # nested group { - die "$ATTN undefined group $item\n" unless $groups{$item}; + die "$ABRT undefined group $item\n" unless $groups{$item}; # add those names to the list push @new_list, sort keys %{ $groups{$item} }; } @@ -185,7 +186,7 @@ sub parse_conf_file # the group was *first* created by using $fragment as the *value* do { $groups{$1}{$_} ||= $fragment } for ( expand_list( split(' ', $2) ) ); # again, we take the more "relaxed" pattern - die "$ATTN bad group $1\n" unless $1 =~ $REPONAME_PATT; + die "$ABRT bad group $1\n" unless $1 =~ $REPONAME_PATT; } # repo(s) elsif (/^repo (.*)/) @@ -210,7 +211,7 @@ sub parse_conf_file # expand the user list, unless it is just "@all" @users = expand_list ( @users ) unless (@users == 1 and $users[0] eq '@all'); - do { die "$ATTN bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; + do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; # ok, we can finally populate the %repos hash for my $repo (@repos) # each repo in the current stanza @@ -250,7 +251,7 @@ sub parse_conf_file } else { - die "$ATTN can't make head or tail of '$_'\n"; + die "$ABRT can't make head or tail of '$_'\n"; } } for my $ig (sort keys %ignored) @@ -275,7 +276,7 @@ for my $fragment_file (glob("conf/fragments/*.conf")) my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]); -close $compiled_fh or die "$ATTN close compiled-conf failed: $!\n"; +close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; # ---------------------------------------------------------------------------- # any new repos to be created? @@ -288,7 +289,7 @@ close $compiled_fh or die "$ATTN close compiled-conf failed: $!\n"; # but it turns out not everyone has "modern" gits :) my $git_version = `git --version`; my ($gv_maj, $gv_min, $gv_patchrel) = ($git_version =~ m/git version (\d+)\.(\d+)\.(\d+)/); -die "$ATTN I can't understand $git_version\n" unless ($gv_maj >= 1); +die "$ABRT I can't understand $git_version\n" unless ($gv_maj >= 1); $git_version = $gv_maj*10000 + $gv_min*100 + $gv_patchrel; # now it's "normalised" my $git_too_old = 0; @@ -300,7 +301,7 @@ for my $repo (keys %repos) { unless (-d "$repo.git") { - system("mkdir", "-p", "$repo.git") and die "$ATTN mkdir $repo.git failed: $!\n"; + system("mkdir", "-p", "$repo.git") and die "$ABRT mkdir $repo.git failed: $!\n"; # erm, note that's "and die" not "or die" as is normal in perl wrap_chdir("$repo.git"); system("git --bare init"); @@ -424,11 +425,11 @@ for my $pubkey (glob("*")) for my $user (sort keys %user_list) { next if $user =~ /^(gitweb|daemon|\@all)$/ or $user_list{$user} eq 'has pubkey'; - print STDERR "$ATTN user $user in config, but has no pubkey!\n"; + print STDERR "$WARN user $user in config, but has no pubkey!\n"; } print $newkeys_fh "# gitolite end\n"; -close $newkeys_fh or die "$ATTN close newkeys failed: $!\n"; +close $newkeys_fh or die "$ABRT close newkeys failed: $!\n"; # all done; overwrite the file (use cat to avoid perm changes) system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); From 130478ed93448801059d43a333bc99532493b848 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 5 Nov 2009 22:16:18 +0530 Subject: [PATCH 130/850] easy install: handle upgrades specially - "it's an upgrade" is decided by presence of gitolite.conf (not a pubkey) - admin_name optional (and will be ignored if given) for upgrades plus a lot of comments and some minor text changes --- src/00-easy-install.sh | 215 +++++++++++++++++++++++------------------ 1 file changed, 119 insertions(+), 96 deletions(-) diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 4cc6ff1..5e468d5 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -15,10 +15,18 @@ # command!) set -e +# ---------------------------------------------------------------------- +# temp files +# ---------------------------------------------------------------------- + export tmpgli=tmp-gl-install trap "rm -rf $tmpgli" 0 mkdir -p $tmpgli +# ---------------------------------------------------------------------- +# service functions +# ---------------------------------------------------------------------- + die() { echo "$@"; echo; echo "run $0 again without any arguments for help and tips"; exit 1; } prompt() { # receives two arguments. A short piece of text to be displayed, without @@ -38,15 +46,16 @@ prompt() { } usage() { cat </dev/null -then - prompt " ...adding key to agent..." \ - "you're running ssh-agent. We'll try and do an ssh-add of the - private key we just created, otherwise this key won't get picked up. If - you specified a passphrase in the previous step, you'll get asked for one - now -- type in the same one." + if ssh-add -l &>/dev/null + then + prompt " ...adding key to agent..." \ + "you're running ssh-agent. We'll try and do an ssh-add of the + private key we just created, otherwise this key won't get picked up. If + you specified a passphrase in the previous step, you'll get asked for one + now -- type in the same one." - ssh-add $HOME/.ssh/$admin_name -fi + ssh-add $HOME/.ssh/$admin_name + fi -# MANUAL: you now need to add some lines to the end of your ~/.ssh/config -# file. If the file doesn't exist, create it. Make sure the file is "chmod -# 644". + # MANUAL: you now need to add some lines to the end of your ~/.ssh/config + # file. If the file doesn't exist, create it. Make sure the file is + # "chmod 644". -# The lines to be included look like this: + # The lines to be included look like this: -# host gitolite -# user git -# hostname server -# port 22 -# identityfile ~/.ssh/sitaram + # host gitolite + # user git + # hostname server + # port 22 + # identityfile ~/.ssh/sitaram -echo " + echo " host gitolite - user $user - hostname $host - port $port - identityfile ~/.ssh/$admin_name" > $tmpgli/.gl-stanza + user $user + hostname $host + port $port + identityfile ~/.ssh/$admin_name" > $tmpgli/.gl-stanza -if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null -then - prompt "found gitolite para in ~/.ssh/config; assuming it is correct..." \ - "your \$HOME/.ssh/config already has settings for gitolite. I will - assume they're correct, but if they're not, please edit that file, delete - that paragraph (that line and the following few lines), Ctrl-C, and rerun. + if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null + then + prompt "found gitolite para in ~/.ssh/config; assuming it is correct..." \ + "your \$HOME/.ssh/config already has settings for gitolite. I will + assume they're correct, but if they're not, please edit that file, delete + that paragraph (that line and the following few lines), Ctrl-C, and rerun. - In case you want to check right now (from another terminal) if they're - correct, here's what they are *supposed* to look like: -$(cat $tmpgli/.gl-stanza)" + In case you want to check right now (from another terminal) if they're + correct, here's what they are *supposed* to look like: + $(cat $tmpgli/.gl-stanza)" -else - prompt "creating gitolite para in ~/.ssh/config..." \ - "creating settings for your gitolite access in $HOME/.ssh/config; - these are the lines that will be appended to your ~/.ssh/config: -$(cat $tmpgli/.gl-stanza)" + else + prompt "creating gitolite para in ~/.ssh/config..." \ + "creating settings for your gitolite access in $HOME/.ssh/config; + these are the lines that will be appended to your ~/.ssh/config: + $(cat $tmpgli/.gl-stanza)" - cat $tmpgli/.gl-stanza >> $HOME/.ssh/config - # if the file didn't exist at all, it might have the wrong permissions - chmod 644 $HOME/.ssh/config -fi + cat $tmpgli/.gl-stanza >> $HOME/.ssh/config + # if the file didn't exist at all, it might have the wrong permissions + chmod 644 $HOME/.ssh/config + fi +} # ---------------------------------------------------------------------- -# client side stuff almost done; server side now +# server side # ---------------------------------------------------------------------- # MANUAL: copy the gitolite directories "src", "conf", and "doc" to the @@ -323,42 +344,44 @@ prompt "installing/upgrading..." \ GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") +# determine if this is an upgrade; we decide based on whether a file called +# $GL_ADMINDIR/conf/gitolite.conf exists on the remote side. We can't do this +# till we know the correct value for GL_ADMINDIR +upgrade=0 +if ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf &> /dev/null +then + upgrade=1 + [[ -n $admin_name ]] && echo "looks like an upgrade... not using new key '$admin_name' after all!" +else + [[ -z $admin_name ]] && die "this doesn't look like an upgrade... I need a name for the admin" +fi + # MANUAL: still in the "gitolite-install" directory? Good. Run # "src/install.pl" ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" -# MANUAL: if you're upgrading, just go to your clone of the admin repo, make a -# dummy change, and push. (This assumes that you didn't change the -# admin_name, pubkeys, userids, ports, or whatever, and you ran easy install -# only to upgrade the software). And then you are **done** -- ignore the rest -# of this file for the purposes of an upgrade +# MANUAL: if you're upgrading, run "src/gl-compile-conf" and you're done! -- +# ignore the rest of this file for the purposes of an upgrade -# determine if this is an upgrade; we decide based on whether a pubkey called -# $admin_name.pub exists in $GL_ADMINDIR/keydir on the remote side -upgrade=0 -if ssh -p $port $user@$host cat $GL_ADMINDIR/keydir/$admin_name.pub &> /dev/null -then - prompt "done! +[[ $upgrade == 1 ]] && { + # just compile it, in case the config file's internal format has changed + # and the hooks expect something different + ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" + + prompt "" "done! If you forgot the help message you saw when you first ran this, there's a somewhat generic version of it at the end of this file. Try: tail -30 $0 -" \ - "this looks like an upgrade, based on the fact that a file called - $admin_name.pub already exists in $GL_ADMINDIR/keydir on the server. - - Please go to your clone of the admin repo, make a dummy change (like maybe - add a blank line to something), commit, and push. You're done! - - (This assumes that you didn't change the admin_name, pubkeys, userids, - ports, or whatever, and you ran easy install only to upgrade the - software)." - +" exit 0 +} -fi +# ---------------------------------------------------------------------- +# from here on it's install only +# ---------------------------------------------------------------------- # MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf # and add at least the following lines to it: From c4069dd85f9d48d02b70c157cabcc111bbe8337e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 6 Nov 2009 08:59:32 +0530 Subject: [PATCH 131/850] (please read full commit message) upgrade behaviour changed **upgrades no longer touch the config or the keydir** When you first install gitolite, the easy install script has to do two *distinct* things: * install the software * create and seed the gitolite-admin repo with a minimum config file and the newly created pubkey That's fine for an install, because nothing exists yet anyway. Subsequent invocations of the script should only do the first task (so that gitolite itself can be upgraded), and not attempt to fiddle with the config file and pubkeys. Unfortunately, until now I had not been separating these two activities cleanly enough. For instance, the commit message for 8e47e01 said: IMPORTANT: we assume that $admin_name remains the same in an upgrade -- that's how we detect it is an upgrade! Change that name or his pubkey, and you're toast! Ouch! So now I decided to clean things up. The "Usage" message tells you clearly what to do for an upgrade. Should have been like this from the beginning, but hey we got there eventually :) ---- Code-wise, this is a major refactor of the easy install script. It uses an old forgotten trick to get forward refs for bash functions ;-) and in the process cleans up the flow quite a bit. --- doc/0-INSTALL.mkd | 17 ++ src/00-easy-install.sh | 595 ++++++++++++++++++++++++----------------- 2 files changed, 364 insertions(+), 248 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index c25a69e..c5ef5b1 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -16,6 +16,7 @@ In this document: * advantages over the older install methods * disadvantages * manual install + * upgrades * other notes * next steps @@ -75,6 +76,22 @@ If you don't have bash, it's not very complicated to do it manually. Just open the file `src/00-easy-install.sh` in a nice, syntax coloring, text editor, and follow the instructions marked "MANUAL" :-) +### upgrades + +Upgrading gitolite is easy. + +To upgrade, pull the latest "master" (or other) branch in your gitolite repo +clone, then run the same exact command you ran to do the install, except you +can leave out the last argument. + +And you might want to add a `-q` to speed things up :-) + +Note that this only upgrades the software. Unlike earlier versions, it does +**not** touch the `conf/gitolite.conf` file or the contents of `keydir` in any +way. I decided that it is not possible to **safely** let an upgrade do +something meaningful with them -- fiddling with existing config files (as +opposed to merely creating one which did not exist) is best left to a human. + ### other notes * if you run `src/00-easy-install.sh` without the `-q` option, you will be diff --git a/src/00-easy-install.sh b/src/00-easy-install.sh index 5e468d5..eea3997 100755 --- a/src/00-easy-install.sh +++ b/src/00-easy-install.sh @@ -16,31 +16,82 @@ set -e # ---------------------------------------------------------------------- -# temp files +# bootstrap and main +# ---------------------------------------------------------------------- +if [[ $1 != boot/strap ]] +then + # did someone tell you you can't call functions before they're defined in + # bash? Don't believe everything you hear ;-) + . $0 boot/strap + main "$@" + cleanup + exit 0 +fi + +# ---------------------------------------------------------------------- +# no direct executable statements after this; only functions # ---------------------------------------------------------------------- -export tmpgli=tmp-gl-install -trap "rm -rf $tmpgli" 0 -mkdir -p $tmpgli +main() { + setup_tempdir + + basic_sanity "$@" + + version_info "$@" + + [[ -n $admin_name ]] && setup_local_ssh + + copy_gl # src, conf, etc + + run_install + + [[ $upgrade == 1 ]] && { + # just compile it, in case the config file's internal format has + # changed and the hooks expect something different + ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" + + eval "echo \"$v_done\"" + cleanup + exit 0 + } + + initial_conf_key + + setup_pta +} + +# ---------------------------------------------------------------------- +# setup temp files +# ---------------------------------------------------------------------- + +setup_tempdir() { + export tmpgli=tmp-gl-install + trap cleanup 0 + mkdir -p $tmpgli +} + +cleanup() { + rm -rf $tmpgli +} # ---------------------------------------------------------------------- # service functions # ---------------------------------------------------------------------- -die() { echo "$@"; echo; echo "run $0 again without any arguments for help and tips"; exit 1; } +die() { echo "$@"; echo; echo "run $0 without any arguments for help and tips"; cleanup; exit 1; } prompt() { # receives two arguments. A short piece of text to be displayed, without # pausing, in "quiet" mode, and a much longer one to be displayed, *with* # a pause, in normal (verbose) mode [[ $quiet == -q ]] && [[ -n $1 ]] && { - echo "$1" + eval "echo \"$1\"" return } shift echo echo echo ------------------------------------------------------------------------ - echo " $1" + eval "echo \"$1\"" echo read -p '...press enter to continue or Ctrl-C to bail out' } @@ -55,7 +106,8 @@ Usage: $0 [-q] user host [port] admin_name # install - "host" is that server's hostname (or IP address) - "port" is the ssh server port on "host"; optional, defaults to 22 - "admin_name" is *your* name as it should appear in the eventual gitolite - config file (not needed/used for upgrades) + config file. For upgrades (ie., gitolite is already installed on the + server), this argument is not needed, and will be *ignored* if provided. Example usage: $0 git my.git.server sitaram @@ -84,116 +136,96 @@ EOFU # basic sanity / argument checks # ---------------------------------------------------------------------- -# MANUAL: this *must* be run as "src/00-easy-install.sh", not by cd-ing to src -# and then running "./00-easy-install.sh" +basic_sanity() { + # MANUAL: this *must* be run as "src/00-easy-install.sh", not by cd-ing to + # src and then running "./00-easy-install.sh" -[[ $0 =~ ^src/00-easy-install.sh$ ]] || -{ - echo "please cd to the gitolite repo top level directory and run this as - 'src/00-easy-install.sh'" - exit 1; + [[ $0 =~ ^src/00-easy-install.sh$ ]] || + { + die "please cd to the gitolite repo top level directory and run this as + 'src/00-easy-install.sh'" + } + + # are we in quiet mode? + quiet= + [[ "$1" == "-q" ]] && { + quiet=-q + shift + } + + # MANUAL: (info) we'll use "git" as the user, "server" as the host, and + # "sitaram" as the admin_name in example commands shown below, if any + + [[ -z $2 ]] && usage + user=$1 + host=$2 + port=22 + admin_name=$3 + # but if the 3rd arg is a number, that's a port number, and the 4th arg is + # the admin_name + if [[ $3 =~ ^[0-9]+$ ]] + then + port=$3 + admin_name=$4 + fi + + [[ "$user" =~ [^a-zA-Z0-9._-] ]] && die "user '$user' invalid" + [[ -n $admin_name ]] && [[ "$admin_name" =~ [^a-zA-Z0-9._-] ]] && die "admin_name '$admin_name' invalid" + + # MANUAL: make sure you're in the gitolite directory, at the top level. + # The following files should all be visible: + + ls src/gl-auth-command \ + src/gl-compile-conf \ + src/install.pl \ + src/update-hook.pl \ + conf/example.conf \ + conf/example.gitolite.rc >/dev/null || + die "cant find at least some files in gitolite sources/config; aborting" + + # MANUAL: make sure you have password-less (pubkey) auth on the server. + # That is, running "ssh git@server" should log in straight away, without + # asking for a password + + ssh -p $port -o PasswordAuthentication=no $user@$host true || + die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something" } -# are we in quiet mode? -quiet= -[[ "$1" == "-q" ]] && { - quiet=-q - shift -} - -# MANUAL: (info) we'll use "git" as the user, "server" as the host, and -# "sitaram" as the admin_name in example commands shown below, if any - -[[ -z $2 ]] && usage -user=$1 -host=$2 -port=22 -admin_name=$3 -# but if the 3rd arg is a number, that's a port number, and the 4th arg is the -# admin_name -if [[ $3 =~ ^[0-9]+$ ]] -then - port=$3 - admin_name=$4 -fi - -[[ "$user" =~ [^a-zA-Z0-9._-] ]] && die "user '$user' invalid" -[[ -n $admin_name ]] && [[ "$admin_name" =~ [^a-zA-Z0-9._-] ]] && die "admin_name '$admin_name' invalid" - -# MANUAL: make sure you're in the gitolite directory, at the top level. -# The following files should all be visible: - -ls src/gl-auth-command \ - src/gl-compile-conf \ - src/install.pl \ - src/update-hook.pl \ - conf/example.conf \ - conf/example.gitolite.rc >/dev/null || - die "cant find at least some files in gitolite sources/config; aborting" - -# MANUAL: make sure you have password-less (pubkey) auth on the server. That -# is, running "ssh git@server" should log in straight away, without asking for -# a password - -ssh -p $port -o PasswordAuthentication=no $user@$host true || - die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something" - # ---------------------------------------------------------------------- # version info # ---------------------------------------------------------------------- -# MANUAL: if needed, make a note of the version you are upgrading from, and to +version_info() { -# record which version is being sent across; we assume it's HEAD -git describe --tags --long HEAD 2>/dev/null > src/VERSION || echo '(unknown)' > src/VERSION + # MANUAL: if needed, make a note of the version you are upgrading from, and to -# what was the old version there? -export upgrade_details="you are upgrading from \ -$(ssh -p $port $user@$host cat gitolite-install/src/VERSION 2>/dev/null || echo '(unknown)' ) \ -to $(cat src/VERSION)" + # record which version is being sent across; we assume it's HEAD + git describe --tags --long HEAD 2>/dev/null > src/VERSION || echo '(unknown)' > src/VERSION -prompt "$upgrade_details" \ - "$upgrade_details + # what was the old version there? + export upgrade_details="you are upgrading from \ + $(ssh -p $port $user@$host cat gitolite-install/src/VERSION 2>/dev/null || echo '(unknown)' ) \ + to $(cat src/VERSION)" - Note: getting '(unknown)' for the 'from' version should only happen once. - Getting '(unknown)' for the 'to' version means you are probably installing - from a tar file dump, not a real clone. This is not an error but it's - nice to have those version numbers in case you need support. Try and - install from a clone" + prompt "$upgrade_details" "$v_upgrade_details" +} # ---------------------------------------------------------------------- # new keypair, ssh-config para; only on "install" (not upgrade) # ---------------------------------------------------------------------- -[[ -n $admin_name ]] && { +setup_local_ssh() { # MANUAL: create a new key for you as a "gitolite user" (as opposed to you # as the "gitolite admin" who needs to login to the server and get a # command line). For example, "ssh-keygen -t rsa ~/.ssh/sitaram"; this # would create two files in ~/.ssh (sitaram and sitaram.pub) - prompt "setting up keypair..." \ - "the next command will create a new keypair for your gitolite access - - The pubkey will be $HOME/.ssh/$admin_name.pub. You will have to choose a - passphrase or hit enter for none. I recommend not having a passphrase for - now, *especially* if you do not have a passphrase for the key which you - are already using to get server access! - - Add one using 'ssh-keygen -p' after all the setup is done and you've - successfully cloned and pushed the gitolite-admin repo. After that, - install 'keychain' or something similar, and add the following command to - your bashrc (since this is a non-default key) - - ssh-add \$HOME/.ssh/$admin_name - - This makes using passphrases very convenient." + prompt "setting up keypair..." "$v_setting_up_keypair" if [[ -f $HOME/.ssh/$admin_name.pub ]] then - prompt " ...reusing $HOME/.ssh/$admin_name.pub..." \ - "Hmmm... pubkey $HOME/.ssh/$admin_name.pub exists; should I just re-use it? - Be sure you remember the passphrase, if you gave one when you created it!" + prompt " ...reusing $HOME/.ssh/$admin_name.pub..." "$v_reuse_pubkey" else ssh-keygen -t rsa -f $HOME/.ssh/$admin_name || die "ssh-keygen failed for some reason..." fi @@ -210,12 +242,7 @@ prompt "$upgrade_details" \ if ssh-add -l &>/dev/null then - prompt " ...adding key to agent..." \ - "you're running ssh-agent. We'll try and do an ssh-add of the - private key we just created, otherwise this key won't get picked up. If - you specified a passphrase in the previous step, you'll get asked for one - now -- type in the same one." - + prompt " ...adding key to agent..." "$v_ssh_add" ssh-add $HOME/.ssh/$admin_name fi @@ -231,30 +258,17 @@ prompt "$upgrade_details" \ # port 22 # identityfile ~/.ssh/sitaram - echo " -host gitolite - user $user - hostname $host - port $port - identityfile ~/.ssh/$admin_name" > $tmpgli/.gl-stanza + echo "host gitolite + user $user + hostname $host + port $port + identityfile ~/.ssh/$admin_name" > $tmpgli/.gl-stanza if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null then - prompt "found gitolite para in ~/.ssh/config; assuming it is correct..." \ - "your \$HOME/.ssh/config already has settings for gitolite. I will - assume they're correct, but if they're not, please edit that file, delete - that paragraph (that line and the following few lines), Ctrl-C, and rerun. - - In case you want to check right now (from another terminal) if they're - correct, here's what they are *supposed* to look like: - $(cat $tmpgli/.gl-stanza)" - + prompt "found gitolite para in ~/.ssh/config; assuming it is correct..." "$v_found_para" else - prompt "creating gitolite para in ~/.ssh/config..." \ - "creating settings for your gitolite access in $HOME/.ssh/config; - these are the lines that will be appended to your ~/.ssh/config: - $(cat $tmpgli/.gl-stanza)" - + prompt "creating gitolite para in ~/.ssh/config..." "$v_creating_para" cat $tmpgli/.gl-stanza >> $HOME/.ssh/config # if the file didn't exist at all, it might have the wrong permissions chmod 644 $HOME/.ssh/config @@ -265,118 +279,83 @@ host gitolite # server side # ---------------------------------------------------------------------- -# MANUAL: copy the gitolite directories "src", "conf", and "doc" to the -# server, to a directory called (for example) "gitolite-install". You may -# have to create the directory first. +copy_gl() { -ssh -p $port $user@$host mkdir -p gitolite-install -rsync $quiet -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ -rm -f src/VERSION + # MANUAL: copy the gitolite directories "src", "conf", and "doc" to the + # server, to a directory called (for example) "gitolite-install". You may + # have to create the directory first. -# MANUAL: now log on to the server (ssh git@server) and get a command line. -# This step is for your convenience; the script does it all from the client -# side but that may be too much typing for manual use ;-) + ssh -p $port $user@$host mkdir -p gitolite-install + rsync $quiet -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ + rm -f src/VERSION -# MANUAL: cd to the "gitolite-install" directory where the sources are. Then -# copy conf/example.gitolite.rc as ~/.gitolite.rc and edit it if you wish to -# change any paths. Make a note of the GL_ADMINDIR and REPO_BASE paths; you -# will need them later + # MANUAL: now log on to the server (ssh git@server) and get a command + # line. This step is for your convenience; the script does it all from + # the client side but that may be too much typing for manual use ;-) -prompt "finding/creating gitolite rc..." \ - "the gitolite rc file needs to be edited by hand. The defaults - are sensible, so if you wish, you can just exit the editor. + # MANUAL: cd to the "gitolite-install" directory where the sources are. + # Then copy conf/example.gitolite.rc as ~/.gitolite.rc and edit it if you + # wish to change any paths. Make a note of the GL_ADMINDIR and REPO_BASE + # paths; you will need them later - Otherwise, make any changes you wish and save it. Read the comments to - understand what is what -- the rc file's documentation is inline. + prompt "finding/creating gitolite rc..." "$v_edit_glrc" - Please remember this file will actually be copied to the server, and that - all the paths etc. represent paths on the server!" - -# lets try and get the file from there first -if scp -P $port $user@$host:.gitolite.rc $tmpgli &>/dev/null -then - prompt " ...trying to reuse existing rc" \ - "Oh hey... you already had a '.gitolite.rc' file on the server. - Let's see if we can use that instead of the default one..." - sort < $tmpgli/.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.old - sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.new - if diff -u $tmpgli/glrc.old $tmpgli/glrc.new + # lets try and get the file from there first + if scp -P $port $user@$host:.gitolite.rc $tmpgli &>/dev/null then - [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc + prompt " ...trying to reuse existing rc" \ + "Oh hey... you already had a '.gitolite.rc' file on the server. +Let's see if we can use that instead of the default one..." + sort < $tmpgli/.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.old + sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.new + if diff -u $tmpgli/glrc.old $tmpgli/glrc.new + then + [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc + else + # MANUAL: if you're upgrading, read the instructions below and + # manually make sure your final ~/.gitolite.rc has both your existing + # customisations as well as any new variables that the new version of + # gitolite has introduced + prompt "" "$v_upgrade_glrc" + ${VISUAL:-${EDITOR:-vi}} conf/example.gitolite.rc $tmpgli/.gitolite.rc + fi else - # MANUAL: if you're upgrading, read the instructions below and - # manually make sure your final ~/.gitolite.rc has both your existing - # customisations as well as any new variables that the new version of - # gitolite has introduced - prompt "" \ - " looks like you're upgrading, and there are some new rc variables - that this version is expecting that your old rc file doesn't have. - - I'm going to run your editor with two filenames. The first is the - example file from this gitolite version. It will have a block (code - and comments) for each of the variables shown above with a '+' sign. - - The second is your current rc file, the destination. Copy those lines - into this file, preferably *with* the surrounding comments (for - clarity) and save it. - - This is necessary; please dont skip this! - - [It's upto you to figure out how your editor handles 2 filename - arguments, switch between them, copy lines, etc ;-)]" - - ${VISUAL:-${EDITOR:-vi}} conf/example.gitolite.rc $tmpgli/.gitolite.rc + cp conf/example.gitolite.rc $tmpgli/.gitolite.rc + [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc fi -else - cp conf/example.gitolite.rc $tmpgli/.gitolite.rc - [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc -fi -# copy the rc across -scp $quiet -P $port $tmpgli/.gitolite.rc $user@$host: + # copy the rc across + scp $quiet -P $port $tmpgli/.gitolite.rc $user@$host: +} -prompt "installing/upgrading..." \ - "ignore any 'please edit this file' or 'run this command' type - lines in the next set of command outputs coming up. They're only relevant - for a manual install, not this one..." +run_install() { -# extract the GL_ADMINDIR and REPO_BASE locations -GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") -REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") + prompt "installing/upgrading..." "$v_ignore_stuff" -# determine if this is an upgrade; we decide based on whether a file called -# $GL_ADMINDIR/conf/gitolite.conf exists on the remote side. We can't do this -# till we know the correct value for GL_ADMINDIR -upgrade=0 -if ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf &> /dev/null -then - upgrade=1 - [[ -n $admin_name ]] && echo "looks like an upgrade... not using new key '$admin_name' after all!" -else - [[ -z $admin_name ]] && die "this doesn't look like an upgrade... I need a name for the admin" -fi + # extract the GL_ADMINDIR and REPO_BASE locations + GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") + REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") -# MANUAL: still in the "gitolite-install" directory? Good. Run -# "src/install.pl" + # determine if this is an upgrade; we decide based on whether a file + # called $GL_ADMINDIR/conf/gitolite.conf exists on the remote side. We + # can't do this till we know the correct value for GL_ADMINDIR + upgrade=0 + if ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf &> /dev/null + then + upgrade=1 + [[ -n $admin_name ]] && echo -e "\n *** WARNING ***: looks like an upgrade... ignoring argument '$admin_name'" + else + [[ -z $admin_name ]] && die " *** ERROR ***: doesn't look like an upgrade, so I need a name for the admin" + fi -ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" + # MANUAL: still in the "gitolite-install" directory? Good. Run + # "src/install.pl" -# MANUAL: if you're upgrading, run "src/gl-compile-conf" and you're done! -- -# ignore the rest of this file for the purposes of an upgrade + ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" -[[ $upgrade == 1 ]] && { - # just compile it, in case the config file's internal format has changed - # and the hooks expect something different - ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" + # MANUAL: if you're upgrading, run "src/gl-compile-conf" and you're done! + # -- ignore the rest of this file for the purposes of an upgrade - prompt "" "done! - - If you forgot the help message you saw when you first ran this, there's a - somewhat generic version of it at the end of this file. Try: - - tail -30 $0 -" - exit 0 } # ---------------------------------------------------------------------- @@ -389,7 +368,8 @@ ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" # repo gitolite-admin # RW+ = sitaram -echo "#gitolite conf +initial_conf_key() { + echo "#gitolite conf # please see conf/example.conf for details on syntax and features repo gitolite-admin @@ -400,57 +380,176 @@ repo testing " > $tmpgli/gitolite.conf -# send the config and the key to the remote -scp $quiet -P $port $tmpgli/gitolite.conf $user@$host:$GL_ADMINDIR/conf/ -scp $quiet -P $port $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir + # send the config and the key to the remote + scp $quiet -P $port $tmpgli/gitolite.conf $user@$host:$GL_ADMINDIR/conf/ + scp $quiet -P $port $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir -# MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" -ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" + # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" + ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" +} # ---------------------------------------------------------------------- # hey lets go the whole hog on this; setup push-to-admin! # ---------------------------------------------------------------------- -# MANUAL: you have to now make the first commit in the admin repo. This is -# a little more complex, so read carefully and substitute the correct paths. -# What you have to do is: +setup_pta() { -# cd $REPO_BASE/gitolite-admin.git -# GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir -# GIT_WORK_TREE=$GL_ADMINDIR git commit -am start + # MANUAL: you have to now make the first commit in the admin repo. This + # is a little more complex, so read carefully and substitute the correct + # paths. What you have to do is: -# Substitute $GL_ADMINDIR and $REPO_BASE appropriately. Note there is no -# space around the "=" in the second and third lines. + # cd $REPO_BASE/gitolite-admin.git + # GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir + # GIT_WORK_TREE=$GL_ADMINDIR git commit -am start -echo "cd $REPO_BASE/gitolite-admin.git + # Substitute $GL_ADMINDIR and $REPO_BASE appropriately. Note there is no + # space around the "=" in the second and third lines. + + echo "cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty " | ssh -p $port $user@$host -# MANUAL: now that the admin repo is created, you have to set the hooks -# properly. The install program does this. So cd back to the -# "gitolite-install" directory and run "src/install.pl" + # MANUAL: now that the admin repo is created, you have to set the hooks + # properly. The install program does this. So cd back to the + # "gitolite-install" directory and run "src/install.pl" -ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" + ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" -# MANUAL: you're done! Log out of the server, come back to your workstation, -# and clone the admin repo using "git clone gitolite:gitolite-admin.git", or -# pull once again if you already have a clone + # MANUAL: you're done! Log out of the server, come back to your + # workstation, and clone the admin repo using "git clone + # gitolite:gitolite-admin.git", or pull once again if you already have a + # clone -prompt "cloning gitolite-admin repo..." \ -"now we will clone the gitolite-admin repo to your workstation - and see if it all hangs together. We'll do this in your \$HOME for now, - and you can move it elsewhere later if you wish to." + prompt "cloning gitolite-admin repo..." "$v_cloning" -cd $HOME -git clone gitolite:gitolite-admin.git + cleanup + cd $HOME + git clone gitolite:gitolite-admin.git -# MANUAL: be sure to read the message below; this applies to you too... + # MANUAL: be sure to read the message below; this applies to you too... -echo -echo -echo ------------------------------------------------------------------------ -echo " + echo + echo + echo --------------------------------------------------------------- + eval "echo \"$tail\"" +} + +# ---------------------------------------------------------------------- +# prompt strings +# ---------------------------------------------------------------------- + +v_upgrade_details=" +\$upgrade_details + +Note: getting '(unknown)' for the 'from' version should only happen once. +Getting '(unknown)' for the 'to' version means you are probably installing +from a tar file dump, not a real clone. This is not an error but it's nice to +have those version numbers in case you need support. Try and install from a +clone +" + +v_setting_up_keypair=" +the next command will create a new keypair for your gitolite access + +The pubkey will be \$HOME/.ssh/\$admin_name.pub. You will have to choose a +passphrase or hit enter for none. I recommend not having a passphrase for +now, *especially* if you do not have a passphrase for the key which you are +already using to get server access! + +Add one using 'ssh-keygen -p' after all the setup is done and you've +successfully cloned and pushed the gitolite-admin repo. After that, install +'keychain' or something similar, and add the following command to your bashrc +(since this is a non-default key) + + ssh-add \\\$HOME/.ssh/\$admin_name + +This makes using passphrases very convenient. +" + +v_reuse_pubkey=" +Hmmm... pubkey \$HOME/.ssh/\$admin_name.pub exists; should I just re-use it? +Be sure you remember the passphrase, if you gave one when you created it! +" + +v_ssh_add=" +you're running ssh-agent. We'll try and do an ssh-add of the +private key we just created, otherwise this key won't get picked up. If +you specified a passphrase in the previous step, you'll get asked for one +now -- type in the same one. +" + +v_found_para=" +your \\\$HOME/.ssh/config already has settings for gitolite. I will assume +they're correct, but if they're not, please edit that file, delete that +paragraph (that line and the following few lines), Ctrl-C, and rerun. + +In case you want to check right now (from another terminal) if they're +correct, here's what they are *supposed* to look like: + +\$(cat \$tmpgli/.gl-stanza) + +" + +v_creating_para=" +creating settings for your gitolite access in \$HOME/.ssh/config; +these are the lines that will be appended to your ~/.ssh/config: + +\$(cat \$tmpgli/.gl-stanza) + +" + +v_edit_glrc=" +the gitolite rc file needs to be edited by hand. The defaults are sensible, +so if you wish, you can just exit the editor. + +Otherwise, make any changes you wish and save it. Read the comments to +understand what is what -- the rc file's documentation is inline. + +Please remember this file will actually be copied to the server, and that all +the paths etc. represent paths on the server! +" + +v_upgrade_glrc=" +looks like you're upgrading, and there are some new rc variables that this +version is expecting that your old rc file doesn't have. + +I'm going to run your editor with two filenames. The first is the example +file from this gitolite version. It will have a block (code and comments) for +each of the variables shown above with a '+' sign. + +The second is your current rc file, the destination. Copy those lines into +this file, preferably *with* the surrounding comments (for clarity) and save +it. + +This is necessary; please dont skip this! + +[It's upto you to figure out how your editor handles 2 filename arguments, +switch between them, copy lines, etc ;-)] +" + +v_ignore_stuff=" +ignore any 'please edit this file' or 'run this command' type lines in the +next set of command outputs coming up. They're only relevant for a manual +install, not this one... +" + +v_done=" +done! + +If you forgot the help message you saw when you first ran this, there's a +somewhat generic version of it at the end of this file. Try: + + tail -31 \$0 +" + +v_cloning=" +now we will clone the gitolite-admin repo to your workstation and see if it +all hangs together. We'll do this in your \\\$HOME for now, and you can move +it elsewhere later if you wish to. +" + +tail=" All done! The admin repo is currently cloned at ~/gitolite-admin; you can clone it @@ -472,7 +571,7 @@ hosting your gitolite setup -- one to get you a command line, and one to get you gitolite access; see doc/6-ssh-troubleshooting.mkd. If you're not using keychain or some such software, you may have to run this each time you log in: - ssh-add ~/.ssh/$admin_name + ssh-add ~/.ssh/\$admin_name URLS: *Your* URL for cloning any repo on this server will be @@ -480,5 +579,5 @@ URLS: *Your* URL for cloning any repo on this server will be *Other* users you set up will have to use - $user@$host:reponame.git + \$user@\$host:reponame.git " From 4d9c064a7a4569df18fb04bdcf278acac923817a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 7 Nov 2009 10:43:52 +0530 Subject: [PATCH 132/850] new program for emergency addkey; run without args for usage --- src/99-emergency-addkey.sh | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100755 src/99-emergency-addkey.sh diff --git a/src/99-emergency-addkey.sh b/src/99-emergency-addkey.sh new file mode 100755 index 0000000..d488798 --- /dev/null +++ b/src/99-emergency-addkey.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# what/why: re-establish gitolite admin access when admin key(s) lost +# where: on server (NOT client!) + +# pre-req: shell access to the server (even with password is fine) +# pre-work: - make yourself a new keypair on your workstation +# - copy the pubkey and this script to the server + +# usage: $0 admin_name client_host_shortname pubkeyfile +# notes: - admin_name should already have RW or RW+ access to the +# gitolite-admin repo +# - client_host_shortname is any simple word; see example below + +# WARNING: ABSOLUTELY NO ARGUMENT CHECKING DONE +# WARNING: NEWER GITS ONLY ON SERVER SIDE (for now) + +# example: $0 sitaram laptop /tmp/sitaram.pub +# result: a new keyfile named sitaram@laptop.pub would be added + +# ENDHELP + +[[ -z $1 ]] && { perl -pe "s(\\\$0)($0); last if /ENDHELP/" < $0; exit 1; } + +set -e + +cd +REPO_BASE=$( perl -e 'do ".gitolite.rc"; print $REPO_BASE' ) +GL_ADMINDIR=$(perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR') + +cd; cd $GL_ADMINDIR/keydir; pwd +cp -v $3 $1@$2.pub + +cd; cd $REPO_BASE/gitolite-admin.git; pwd +# XXX FIXME TODO -- fix this to work with older gits also +GIT_WORK_TREE=$GL_ADMINDIR git add keydir +GIT_WORK_TREE=$GL_ADMINDIR git commit -m "emergency add $1@$2.pub" + +cd $GL_ADMINDIR +src/gl-compile-conf From be972d04d0f714c2e5f5d007b61a3aef743bcd25 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 12 Nov 2009 07:17:37 +0530 Subject: [PATCH 133/850] doc/6: added two keys explanation and workaround --- doc/6-ssh-troubleshooting.mkd | 65 +++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index efd5f2b..2bc04a0 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -9,6 +9,7 @@ In this document: * explanation * files on the server * files on client + * why two keys on client * more complex ssh setups * two gitolite servers to manage? * further reading @@ -169,6 +170,70 @@ Here's how it all hangs together. now works as expected, invoking the special keypair instead of the default one. + + +#### why two keys on client + +Why do I (the admin) need two **different** keypairs? + +There are two types of access the admin will make to the server: a normal +login, to get a shell prompt, and gitolite access (clone/fetch/push etc). The +first access needs an authkeys line *without* any "command=" restrictions, +while the second requires a line *with* such a restriction. + +And we can't use the same key for both because there is no way to disambiguate +them; the ssh server will always (*always*) pick the first one in sequence +when the key is offered by the ssh client. + +So the next question is usually "I have other ways to get a shell on that +account, so why do I need a key for shell access at all?" + +The answer to this is that the "easy install" script, being written for the +most general case, needs shell access via ssh to do its stuff. + +If you really, really, want to get rid of the extra key, here's a transcript +that should have enough info to get you going (but it helps to know ssh well): + + * on "sitaram" user, on my workstation + + cd ~/.ssh + cp id_rsa sitaram + cp id_rsa.pub sitaram.pub + cd ~/gitolite-clone + src/00-easy-install.sh -q git my.git.server sitaram + + that last command produces something like the following: + + you are upgrading from (unknown) to v0.80-6-gdde8c4e + setting up keypair... + ...reusing /home/sitaram/.ssh/sitaram.pub... + creating gitolite para in ~/.ssh/config... + finding/creating gitolite rc... + installing/upgrading... + Pseudo-terminal will not be allocated because stdin is not a terminal. + [master (root-commit) e717a89] start + 2 files changed, 11 insertions(+), 0 deletions(-) + create mode 100644 conf/gitolite.conf + create mode 100644 keydir/sitaram.pub + cloning gitolite-admin repo... + Initialized empty Git repository in /home/sitaram/gitolite-admin/.git/ + fatal: 'gitolite-admin.git' does not appear to be a git repository + fatal: The remote end hung up unexpectedly + + notice that the final step (the clone of the newly created gitolite-admin + repo) failed, as expected + + * now log on to the git hosting account (`git@my.git.server` in this + example), edit `~/.ssh/authorized_keys`, and delete the line with the + first occurrence of your key (this should be *before* the `# gitolite + start` line) + + * now go back to your workstation and + + git clone git@my.git.server:gitolite-admin + +That should do it. + ### more complex ssh setups What do you need to know in order to create more complex ssh setups (for From e81264d1001afcf8847012a28919c5e2b7964fcd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 12 Nov 2009 14:49:39 +0530 Subject: [PATCH 134/850] compile: added repo descriptions example line in config file: gitolite = "fast, secure, access control for git in a corporate environment" --- src/gl-compile-conf | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 3d8177b..3306b0d 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -106,6 +106,9 @@ my %rurp_seen = (); # catch usernames<->pubkeys mismatches; search for "lint" below my %user_list = (); +# gitweb descriptions, plain text, keyed by repo +my %desc = (); + # set the umask before creating any files umask($REPO_UMASK); @@ -249,6 +252,15 @@ sub parse_conf_file } } } + # very simple syntax for the gitweb description of repo + elsif (/^(\S+) = "(.*)"$/) + { + my ($repo, $desc) = ($1, $2); + die "$ABRT bad repo name $repo\n" unless $repo =~ $REPONAME_PATT; + die "$WARN $fragment attempting to set description for $repo\n" if + $fragment ne 'master' and $fragment ne $repo and ($groups{"\@$fragment"}{$repo} || '') ne 'master'; + $desc{$repo} = $desc; + } else { die "$ABRT can't make head or tail of '$_'\n"; @@ -359,13 +371,17 @@ for my $repo (sort keys %repos) { # ...then gitwebs for my $repo (sort keys %repos) { - if ($repos{$repo}{'R'}{'gitweb'}) { + my $desc_file = "$repo.git/description"; + # note: having a description also counts as enabling gitweb + if ($repos{$repo}{'R'}{'gitweb'} or $desc{$repo}) { unless ($projlist{"$repo.git"}) { # not in the old list; add it to the new one $projlist{"$repo.git"} = 1; $projlist_changed = 1; print "gitweb add $repo.git\n"; } + # add the description file; no messages to user or error checking :) + open(DESC, ">", $desc_file) and print DESC "$desc{$repo}\n" and close DESC; } else { if ($projlist{"$repo.git"}) { # delete it from new list @@ -373,6 +389,8 @@ for my $repo (sort keys %repos) { $projlist_changed = 1; print "gitweb del $repo.git\n"; } + # delete the description file; no messages to user or error checking :) + unlink $desc_file; } } From 76520693a336f3a5cd3e56de4ef53189f1b57933 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 12 Nov 2009 18:53:49 +0530 Subject: [PATCH 135/850] doc/2: add docs for gitweb description, plus some minor cleanup --- doc/2-admin.mkd | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index ed1d361..240b647 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -38,22 +38,27 @@ This is a feature that I personally do not use (corporate environments don't like unauthenticated access of any kind to any repo!), but someone wanted it, so here goes. -There's **no special syntax** for this -- just give read permission to a user -called `gitweb` or `daemon`! (This also means you can't have a normal user -with either of those two names, but I doubt that's a problem!). See the [faq, -tips, etc][ss] document for easy ways to specify access for multiple -repositories. +To make a repo or repo group accessible via "git daemon", just give read +permission to the special user "daemon". See the [faq, tips, etc][ss] +document for easy ways to specify access for multiple repositories. [ss]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gwd -Note that this does **not** install or configure gitweb/daemon -- that is a -one-time setup you must do separately. All this does is: +There's a special user called "gitweb" also, which works the same way. +However, setting a description for the project also enables gitweb permissions +so you may as well use that method and kill two birds with one stone, like so: + gitolite = "fast, secure, access control for git in a corporate environment" + +Note that gitolite does **not** install or configure gitweb/daemon -- that is +a one-time setup you must do separately. All this does is: + + * for daemon, create the file `git-daemon-export-ok` in the repository * for gitweb, add the repo to the list of projects to be served by gitweb (see the config file variable `$PROJECTS_LIST`, which should have the same value you specified for `$projects_list` when setting up gitweb) - * for daemon, create the file `git-daemon-export-ok` in the repository + * put the description, if given, in `$repo/description` The "compile" script will keep these files consistent with the config settings --- this includes removing such settings if you remove "read" permissions for -the special usernames. +-- this includes removing such settings/files if you remove "read" permissions +for the special usernames or remove the description line. From 012d4b1fb62fdb6113842d0f86c9e4aa051fe55c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 12 Nov 2009 18:28:08 +0530 Subject: [PATCH 136/850] example conf: total reformat/refactor, include gitweb description --- conf/example.conf | 167 ++++++++++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 74 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 81b6148..752c279 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -1,9 +1,11 @@ # example conf file for gitolite +# ---------------------------------------------------------------------------- # overall syntax: -# - everything in this is space-separated; no commas, semicolons, etc +# - everything is space-separated; no commas, semicolons, etc (except in +# the description string for gitweb) # - comments in the normal shell-ish style; no surprises there -# - there are no continuation lines of any kind +# - there are NO continuation lines of any kind # - user/repo names as simple as possible # (usernames: only alphanumerics, ".", "_", "-"; # reponames: same, plus "/", but not at the start) @@ -14,56 +16,79 @@ # - specify who can push a branch/tag # - specify who can rewind a branch/rewrite a tag -# convenience: allow specifying the access control in bits and pieces, even if -# they overlap. Keeps the config file smaller and saner. See the example in -# the "faq, tips, etc" document - # ---------------------------------------------------------------------------- -# LISTS + +# GROUPS +# ------ # syntax: -# @listname = name [...] -# lists can be used as shorthand for usernames as well as reponames +# @groupname = [one or more names] -# a list is equivalent to typing out all the right hand side names, so why do -# we need lists at all? (1) to be able to reuse the same set of usernames in -# the paras for different repos, (2) to keep the lines short, because lists -# accumulate, like squid ACLs, so you can say: +# groups let you club names together for convenience in specifying +# permissions. A group is simply expanded to whatever names are on the right +# hand side when it is actually used -# @cust_A = cust1 cust2 -# @cust_A = cust99 + # you can have a group of people... +@staff = sitaram some_dev another-dev -# and this is the same as listing all three on the same line + # ...or a group of repos +@oss_repos = gitolite linux git perl rakudo entrans vkc -# you can nest groups, but not recursively of course! + # even sliced and diced differently +@admins = sitaram admin2 + # notice that sitaram is in 2 groups (staff and admins) -# @interns = indy james -# @staff = bob @interns + # if you repeat a group name in another definition line, the + # new ones get added to the old ones (they accumulate) +@staff = au.thor + # so now "@staff" expands to all 4 names -# @staff = me alice -# @secret_staff = bruce whitfield martin + # groups can include other groups (but not recursively) +@interns = indy james +@staff = bob @interns + # "@staff" expands to 7 names now -# @pubrepos = linux git - -# @privrepos = supersecretrepo anothersecretrepo - -# ---------------------------------------------------------------------------- -# REPOS, REFS, and PERMISSIONS +# REPO AND BRANCH PERMISSIONS +# --------------------------- # syntax: -# repo [one or more repos] -# (R|RW|RW+) [zero or more refexes] = [one or more users] +# start line: +# repo [one or more repos and/or repo groups] +# followed by one or more permissions lines: +# (R|RW|RW+) [zero or more refexes] = [one or more users] -# notes: +# there are 3 types of permissions: R, RW, and RW+. The "+" means permission +# to "rewind" (force push a non-fast forward to) a branch -# - the reponame is a simple name. Do not add the ".git" extension -- -# that will be added by the program when the actual repo is created +# how permissions are matched: +# - user, repo, and access (W or +) are known. For that combination, if +# any of the refexes match the refname being updated, the push succeeds. +# If none of them match, it fails -# - RW+ means non-ff push is allowed -# - you can't write just "W" or "+"; it has to be R, or RW, or RW+ +# what's a refex? a regex to match against the ref being updated (get it?) -# - a refex is a regex that matches a ref :-) If you see the examples -# below you'll get it easy enough +# BASIC PERMISSIONS (repo level only; apply to all branches/tags in repo) + + # most important rule of all -- specify who can make changes + # to *this* file take effect +repo gitolite-admin + RW+ = @admins + + # "@all" is a special, predefined, group name +repo testing + RW+ = @all + + # this repo is visible to staff but only sitaram can write to it +repo gitolite + R = @staff + RW+ = sitaram + + # you can split up access rules for a repo as convenient + # (notice that @oss_repos contains gitolite also) +repo @oss_repos + R = @all + +# ADVANCED PERMISSIONS USING REFEXES # - refexes are specified in perl regex syntax # - if no refex appears, the rule applies to all refs in that repo @@ -71,53 +96,47 @@ # with "refs/" (so tags have to be explicitly named as # refs/tags/pattern) -# - the list of users or repos can inlude any group name defined earlier -# - "@all" is a special, predefined, groupname that means "all users" -# (there is no corresponding shortcut for all repos) + # here's the example from + # Documentation/howto/update-hook-example.txt: -# matching: + # refs/heads/master junio + # +refs/heads/pu junio + # refs/heads/cogito$ pasky + # refs/heads/bw/.* linus + # refs/heads/tmp/.* .* + # refs/tags/v[0-9].* junio -# - user, repo, and access (W or +) are known. For that combination, if -# any of the refexes match the refname being updated, the push succeeds. -# If none of them match, it fails + # and here're the equivalent gitolite refexes +repo git + RW master = junio + RW+ pu = junio + RW cogito$ = pasky + RW bw/ = linus + RW tmp/ = @all + RW refs/tags/v[0-9] = junio -# anyone can play in the sandbox, including making non-fastforward commits -# (that's what the "+" means) -# repo sandbox -# RW+ = @all +# GITWEB AND DAEMON STUFF +# ----------------------- -# my repo and alice's repo have the same memberships and access, so we just -# put them both in the same stanza +# No specific syntax for gitweb and daemon access; just make the repo readable +# ("R" access) to the special users "gitweb" and "daemon" -# repo myrepo alicerepo -# RW+ = me alice -# R = bob eve + # make "@oss_repos" (all 7 of them!) accessible via git daemon +repo @oss_repos + R = daemon -# this repo is visible to customers from company A but they can't write to it + # make the two *large* repos accessible via gitweb +repo linux perl + R = gitweb -# repo cust_A_repo -# R = @cust_A -# RW = @staff +# GITWEB DESCRIPTION LINE -# idea for the tags syntax shamelessly copied from git.git -# Documentation/howto/update-hook-example.txt :) +# syntax: +# reponame = "some description string in double quotes" -# repo @privrepos thirdsecretrepo -# RW+ pu = bruce -# RW master next = bruce -# RW refs/tags/v[0-9].* = bruce -# RW refs/tags/ss/ = @secret_staff -# RW tmp/.* = @secret_staff -# R = @secret_staff +# note: setting a description also gives gitweb access; you do not have to +# give gitweb access as described above if you're specifying a description -# ---------------------------------------------------------------------------- -# GITWEB AND DAEMON CONTROL - -# there is no special syntax for this. If a repo gives read permissions to -# the special user "gitweb" or "daemon", the corresponding changes are made -# when you compile; see "faq, tips, etc" document for details. - -# this means you cannot have a real user called "gitweb" or "daemon" but I -# don't think that is a problem :-) +gitolite = "fast, secure, access control for git in a corporate environment" From 448c0d37baec8fac4508af06f211e1e02862c363 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 12 Nov 2009 20:45:49 +0530 Subject: [PATCH 137/850] compile: writing description file should be conditional --- src/gl-compile-conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 3306b0d..ab8774f 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -381,7 +381,7 @@ for my $repo (sort keys %repos) { print "gitweb add $repo.git\n"; } # add the description file; no messages to user or error checking :) - open(DESC, ">", $desc_file) and print DESC "$desc{$repo}\n" and close DESC; + $desc{$repo} and open(DESC, ">", $desc_file) and print DESC "$desc{$repo}\n" and close DESC; } else { if ($projlist{"$repo.git"}) { # delete it from new list From c54d3eabbc4ffe448ee38f91a6431784d7303d3e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 13 Nov 2009 05:03:09 +0530 Subject: [PATCH 138/850] all src: (please read full commit message): allow local admin-defined hooks You can now add your own hooks into src/hooks/ and they get propagated along with the update hook that is present there now. Please read the new section in the admin document, and make sure you understand the security implications of accidentally fiddling with the "update" script. This also prompted a major rename spree of all the files to be consistent, etc. Plus people said that the .sh and .pl suffixes should be avoided (and I was feeling the same way). I've also been inconsistent with that "gl-" prefix, so I cleaned that up, and the 00- and 99- were also funny animals. Time to get all this cleaned up before we get 1.0 :) So these are the changes, in case you're looking at just the commit message and not the diffstat: src/pta-hook.sh -> src/ga-post-update-hook src/conf-convert.pl -> src/gl-conf-convert src/00-easy-install.sh -> src/gl-easy-install src/99-emergency-addkey.sh -> src/gl-emergency-addkey src/install.pl -> src/gl-install src/update-hook.pl -> src/hooks/update --- doc/0-INSTALL.mkd | 8 ++--- doc/1-migrate.mkd | 2 +- doc/2-admin.mkd | 23 ++++++++++++++ doc/6-ssh-troubleshooting.mkd | 4 +-- src/{pta-hook.sh => ga-post-update-hook} | 0 src/gl-compile-conf | 3 +- src/{conf-convert.pl => gl-conf-convert} | 0 src/{00-easy-install.sh => gl-easy-install} | 30 ++++++++++--------- ...mergency-addkey.sh => gl-emergency-addkey} | 0 src/{install.pl => gl-install} | 14 ++++++--- src/{update-hook.pl => hooks/update} | 0 11 files changed, 58 insertions(+), 26 deletions(-) rename src/{pta-hook.sh => ga-post-update-hook} (100%) rename src/{conf-convert.pl => gl-conf-convert} (100%) rename src/{00-easy-install.sh => gl-easy-install} (96%) rename src/{99-emergency-addkey.sh => gl-emergency-addkey} (100%) rename src/{install.pl => gl-install} (82%) rename src/{update-hook.pl => hooks/update} (100%) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index c5ef5b1..984f040 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -39,7 +39,7 @@ Assumptions/pre-requisites: new keypair if needed, then run `ssh-copy-id user@host`) * you have a clone or an archive of gitolite somewhere on your workstation -If so, just `cd` to that clone and run `src/00-easy-install.sh` and follow the +If so, just `cd` to that clone and run `src/gl-easy-install` and follow the prompts! (Running it without any arguments shows you usage plus other useful info). @@ -47,7 +47,7 @@ info). A typical run for me is: - src/00-easy-install.sh -q git my.git.server sitaram + src/gl-easy-install -q git my.git.server sitaram `-q` stands for "quiet" mode -- very minimal output, no verbose descriptions of what it is going to do, and no pauses unless absolutely needed. However, @@ -73,7 +73,7 @@ actually doing, I suggest you skip the `-q`. ### manual install If you don't have bash, it's not very complicated to do it manually. Just -open the file `src/00-easy-install.sh` in a nice, syntax coloring, text +open the file `src/gl-easy-install` in a nice, syntax coloring, text editor, and follow the instructions marked "MANUAL" :-) ### upgrades @@ -94,7 +94,7 @@ opposed to merely creating one which did not exist) is best left to a human. ### other notes - * if you run `src/00-easy-install.sh` without the `-q` option, you will be + * if you run `src/gl-easy-install` without the `-q` option, you will be given a chance to edit `~/.gitolite.rc`. You can change any options (such as paths, for instance), but be sure to keep the perl syntax -- you *don't* have to know perl to do so, it's fairly easy to guess in this diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd index fb5de34..c09fe13 100644 --- a/doc/1-migrate.mkd +++ b/doc/1-migrate.mkd @@ -46,7 +46,7 @@ Now, log off the server and get back to the client: gitosis-admin clone in `$GSAC` below, and similarly the path for your gito**lite**-admin clone in `$GLAC` - src/conf-convert.pl < $GSAC/gitosis.conf > $GLAC/gitolite.conf + src/gl-conf-convert < $GSAC/gitosis.conf > $GLAC/gitolite.conf Be sure to check the file to make sure it converted correctly diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 240b647..7038fb9 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -3,6 +3,13 @@ *Note*: some of the paths in this document use variable names. Just refer to `~/.gitolite.rc` for the correct values for *your* installation. +In this document: + + * administer + * adding users and repos + * specifying gitweb and daemon access + * custom hooks + ### administer First of all, ***do NOT add new repos manually***, unless you know how to add @@ -62,3 +69,19 @@ a one-time setup you must do separately. All this does is: The "compile" script will keep these files consistent with the config settings -- this includes removing such settings/files if you remove "read" permissions for the special usernames or remove the description line. + +#### custom hooks + +If you want to put in your own, custom, hooks every time a new repo is created +by gitolite, put a **tested** hook script in `src/hooks`. As distributed, the +only file there is the `update` hook, but everything (*everything*) in that +directory will get copied to the `hooks/` subdirectory of every *new* repo +created. + +In order to push a new or updated hook script to *existing* repos as well, +just run easy install once again; it'll do it to existing repos also. + +**VERY IMPORTANT SECURITY NOTE: the `update` hook in `src/hooks` is what +implements all the branch-level permissions in gitolite. If you fiddle with +the hooks directory, please make sure you do not mess with this file +accidentally, or all your fancy per-branch permissions will stop working.** diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 2bc04a0..462b59a 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -117,7 +117,7 @@ Here's how it all hangs together. ~/.ssh/id_rsa.pub * gitolite keypair; the "sitaram" in this is the 3rd argument to the - `src/00-easy-install.sh` command you ran; the easy install script does the + `src/gl-easy-install` command you ran; the easy install script does the rest ~/.ssh/sitaram @@ -200,7 +200,7 @@ that should have enough info to get you going (but it helps to know ssh well): cp id_rsa sitaram cp id_rsa.pub sitaram.pub cd ~/gitolite-clone - src/00-easy-install.sh -q git my.git.server sitaram + src/gl-easy-install -q git my.git.server sitaram that last command produces something like the following: diff --git a/src/pta-hook.sh b/src/ga-post-update-hook similarity index 100% rename from src/pta-hook.sh rename to src/ga-post-update-hook diff --git a/src/gl-compile-conf b/src/gl-compile-conf index ab8774f..de3cf00 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -317,7 +317,8 @@ for my $repo (keys %repos) # erm, note that's "and die" not "or die" as is normal in perl wrap_chdir("$repo.git"); system("git --bare init"); - system("cp $GL_ADMINDIR/src/update-hook.pl hooks/update"); + # propagate our own, plus any local admin-defined, hooks + system("cp $GL_ADMINDIR/src/hooks/* hooks/"); chmod 0755, "hooks/update"; wrap_chdir("$repo_base_abs"); $git_too_old++ if $git_version < 10602; # that's 1.6.2 to you diff --git a/src/conf-convert.pl b/src/gl-conf-convert similarity index 100% rename from src/conf-convert.pl rename to src/gl-conf-convert diff --git a/src/00-easy-install.sh b/src/gl-easy-install similarity index 96% rename from src/00-easy-install.sh rename to src/gl-easy-install index eea3997..e5d23da 100755 --- a/src/00-easy-install.sh +++ b/src/gl-easy-install @@ -120,7 +120,7 @@ Notes: Pre-requisites: - you must run this from the gitolite working tree top level directory. - This means you run this as "src/00-easy-install.sh" + This means you run this as "src/gl-easy-install" - you must already have pubkey based access to user@host. If you currently only have password access, use "ssh-copy-id" or something equivalent (or copy the key manually). Somehow (doesn't matter how), get to the point @@ -137,13 +137,13 @@ EOFU # ---------------------------------------------------------------------- basic_sanity() { - # MANUAL: this *must* be run as "src/00-easy-install.sh", not by cd-ing to - # src and then running "./00-easy-install.sh" + # MANUAL: this *must* be run as "src/gl-easy-install", not by cd-ing to + # src and then running "./gl-easy-install" - [[ $0 =~ ^src/00-easy-install.sh$ ]] || + [[ $0 =~ ^src/gl-easy-install$ ]] || { die "please cd to the gitolite repo top level directory and run this as - 'src/00-easy-install.sh'" + 'src/gl-easy-install'" } # are we in quiet mode? @@ -175,11 +175,13 @@ basic_sanity() { # MANUAL: make sure you're in the gitolite directory, at the top level. # The following files should all be visible: - ls src/gl-auth-command \ - src/gl-compile-conf \ - src/install.pl \ - src/update-hook.pl \ - conf/example.conf \ + ls src/ga-post-update-hook \ + src/gitolite.pm \ + src/gl-install \ + src/gl-auth-command \ + src/gl-compile-conf \ + src/hooks/update \ + conf/example.conf \ conf/example.gitolite.rc >/dev/null || die "cant find at least some files in gitolite sources/config; aborting" @@ -349,9 +351,9 @@ run_install() { fi # MANUAL: still in the "gitolite-install" directory? Good. Run - # "src/install.pl" + # "src/gl-install" - ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" + ssh -p $port $user@$host "cd gitolite-install; src/gl-install $quiet" # MANUAL: if you're upgrading, run "src/gl-compile-conf" and you're done! # -- ignore the rest of this file for the purposes of an upgrade @@ -412,9 +414,9 @@ GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty # MANUAL: now that the admin repo is created, you have to set the hooks # properly. The install program does this. So cd back to the - # "gitolite-install" directory and run "src/install.pl" + # "gitolite-install" directory and run "src/gl-install" - ssh -p $port $user@$host "cd gitolite-install; src/install.pl $quiet" + ssh -p $port $user@$host "cd gitolite-install; src/gl-install $quiet" # MANUAL: you're done! Log out of the server, come back to your # workstation, and clone the admin repo using "git clone diff --git a/src/99-emergency-addkey.sh b/src/gl-emergency-addkey similarity index 100% rename from src/99-emergency-addkey.sh rename to src/gl-emergency-addkey diff --git a/src/install.pl b/src/gl-install similarity index 82% rename from src/install.pl rename to src/gl-install index 09a8345..afea0dc 100755 --- a/src/install.pl +++ b/src/gl-install @@ -66,20 +66,26 @@ unless (-f $GL_CONF) { EOF } -# finally, any potential changes to src/update-hook.pl must be propagated to -# all the repos' hook directories +# finally, hooks must be propagated to all the repos in case they changed chdir("$repo_base_abs") or die "chdir $repo_base_abs failed: $!\n"; for my $repo (`find . -type d -name "*.git"`) { chomp ($repo); - system("cp $GL_ADMINDIR/src/update-hook.pl $repo/hooks/update"); + # propagate our own, plus any local admin-defined, hooks + system("cp $GL_ADMINDIR/src/hooks/* $repo/hooks/"); chmod 0755, "$repo/hooks/update"; } # oh and one of those repos is a bit more special and has an extra hook :) if ( -d "gitolite-admin.git/hooks" ) { print "copying post-update hook to gitolite-admin repo...\n"; - system("cp -v $GL_ADMINDIR/src/pta-hook.sh gitolite-admin.git/hooks/post-update"); + system("cp -v $GL_ADMINDIR/src/ga-post-update-hook gitolite-admin.git/hooks/post-update"); system("perl", "-i", "-p", "-e", "s(export GL_ADMINDIR=.*)(export GL_ADMINDIR=$GL_ADMINDIR)", "gitolite-admin.git/hooks/post-update"); chmod 0755, "gitolite-admin.git/hooks/post-update"; } + +# fixup program renames +for my $oldname qw(pta-hook.sh conf-convert.pl 00-easy-install.sh 99-emergency-addkey.sh install.pl update-hook.pl) { + unlink "$GL_ADMINDIR/src/$oldname"; + unlink "$ENV{HOME}/gitolite-install/src/$oldname"; +} diff --git a/src/update-hook.pl b/src/hooks/update similarity index 100% rename from src/update-hook.pl rename to src/hooks/update From e8270e9b7272458b17f8ed2ccb76ec05f5c45b2a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 16 Nov 2009 19:25:34 +0530 Subject: [PATCH 139/850] update hook: 'sub check_ref' to prepare for rebel+ factor out the code to check $ref into a sub; will help rebel+, which wants (horrors!) to restrict based on PATH names too! --- src/hooks/update | 56 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/src/hooks/update b/src/hooks/update index cdf7402..a11b9ad 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -64,22 +64,44 @@ push @allowed_refs, { "$PERSONAL/$ENV{GL_USER}/" => "RW+" } if $PERSONAL; # we want specific perms to override @all, so they come first push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$ENV{GL_USER}} || [] }; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; -for my $ar (@allowed_refs) -{ - my $refex = (keys %$ar)[0]; - # refex? sure -- a regex to match a ref against :) - next unless $ref =~ /$refex/; - if ($ar->{$refex} =~ /\Q$perm/) - { - # if log failure isn't important enough to block pushes, get rid of - # all the error checking - open my $log_fh, ">>", $ENV{GL_LOG} - or die "open log failed: $!\n"; - print $log_fh "$ENV{GL_TS} $perm\t" . - substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . - "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\t$refex\n"; - close $log_fh or die "close log failed: $!\n"; - exit 0; + +my $refex = ''; + +# check one ref +sub check_ref { + + # normally, the $ref will be whatever ref the commit is trying to update + # (like refs/heads/master or whatever). At least one of the refexes that + # pertain to this user must match this ref **and** the corresponding + # permission must also match the action (W or +) being attempted. If none + # of them match, the access is denied. + + # Notice that the function DIES!!! Any future changes that require more + # work to be done *after* this, even on failure, can start using return + # codes etc., but for now we're happy to just die. + + my $ref = shift; + for my $ar (@allowed_refs) { + $refex = (keys %$ar)[0]; + # refex? sure -- a regex to match a ref against :) + next unless $ref =~ /$refex/; + + # as far as *this* ref is concerned we're ok + return if ($ar->{$refex} =~ /\Q$perm/); } + die "$perm $ref $ENV{GL_REPO} $ENV{GL_USER} DENIED by fallthru\n"; } -die "$perm $ref $ENV{GL_REPO} $ENV{GL_USER} DENIED by fallthru\n"; + +# and in this version, we have only one ref to check +check_ref($ref); + +# if we returned at all, the check succeeded, so we log the action and exit 0 + +# logging note: if log failure isn't important enough to block pushes, get rid +# of all the error checking +open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n"; +print $log_fh "$ENV{GL_TS} $perm\t" . + substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . + "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\t$refex\n"; +close $log_fh or die "close log failed: $!\n"; +exit 0; From 05a06a2c75fd6f9172b6cb5fea4825a01331a6a6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 18 Nov 2009 07:18:05 +0530 Subject: [PATCH 140/850] README, doc/3: gitweb "description" feature --- README.mkd | 4 ++-- doc/3-faq-tips-etc.mkd | 26 +++++++++++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/README.mkd b/README.mkd index 660e8f2..e1a9616 100644 --- a/README.mkd +++ b/README.mkd @@ -73,8 +73,8 @@ detail [here][gsdiff]. * config file syntax gets checked upfront, and much more thoroughly * if your requirements are still too complex, you can split up the config file and delegate authority over parts of it - * easier to specify gitweb/daemon access, and easier to link gitweb - authorisation with gitolite + * easier to specify gitweb "description" and gitweb/daemon access + * easier to sync gitweb (http) authorisation with gitolite's access config * more comprehensive logging [aka: management does not think "blame" is just a synonym for "annotate" :-)] * "personal namespace" prefix for each dev diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 64997b1..05eb8b7 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -11,7 +11,7 @@ In this document: * two levels of access rights checking * error checking the config file * delegating parts of the config file - * easier to specify gitweb/daemon access + * easier to specify gitweb "description" and gitweb/daemon access * easier to link gitweb authorisation with gitolite * better logging * one user, many keys @@ -196,17 +196,25 @@ for details. -#### easier to specify gitweb/daemon access +#### easier to specify gitweb "description" and gitweb/daemon access -Which of your repos should be accessible via plain HTTP or the `git://` -protocols (gitweb and git daemon, respectively)? +To enable access to a repo via gitweb *and* create a "description" for it to +show up on the webpage, just add a line like this, anywhere in the config +file: -Specifying gitweb and/or daemon access for a repo is simple: give "read" -permissions to two special usernames: `gitweb` and `daemon`. + reponame = "one line of description" -You can also keep these pieces separate from the detailed, branch level access -for each repo, if you like, since you can write the access control specs in -bits and pieces. Here's an example, using short repo names for convenience: +To enable access to one or more repos via git daemon, just give "read" +permissions to the special username `daemon`. + +There is also a special user called `gitweb` to specify gitweb access; useful +if you don't care about specifying individual descriptions for each repo and +just want to quickly enable gitweb access to one or more repos. + +Remember gitolite lets you specify the access control specs in bits and +pieces, so you can keep all the daemon/gitweb access in one place, even if +each repo has more specific branch-level access config specified elsewhere. +Here's an example, using really short reponames because I'm lazy: # maybe near the top of the file, for ease of access: From c727d68caad769d45935a6d80eddc78ac0c800c7 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 18 Nov 2009 13:37:14 +0530 Subject: [PATCH 141/850] install doc: server and client requirements spelled out --- doc/0-INSTALL.mkd | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 984f040..0ab8c97 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -19,6 +19,7 @@ In this document: * upgrades * other notes * next steps + * appendix: server and client requirements ---- @@ -105,3 +106,72 @@ opposed to merely creating one which did not exist) is best left to a human. The last message produced by the easy install script should tell you how to add users, repos, etc., and you will find more details in the [admin][admin] document. + + + +### appendix: server and client requirements + +There are 3 machines *potentially* involved in installing and administering +gitolite. + +#### server + +This is where gitolite is eventually installed. You need a *normal* userid +(typically "git" but can be anything) on this machine; root access is *not* +needed. + +You need the following software on it: + + * git + * can be in a non-PATH location if you are unable to install it + normally; see the `$GIT_PATH` variable in the "rc" file + * perl + * default install is fine; no special modules are needed + * (a normal install of git also requires/installs perl, so you probably + have it already) + * openssh server + * (I guess any ssh server that can understand the `authorized_keys` file + format should work) + * **bash shell** + * a small part of the gitolite server side is written in "bash"; I + intend to test it (time permitting) on non-bash (like ksh or plain sh) + on non-Linux servers. Once that is done, this bash dependency will go + away. + +#### install workstation + +Installing or upgrading the gitolite software itself is best done by running +the easy-install program from a clone, usually on a different machine. + +This script is heavily dependent on bash, so you need a unix machine that has +a bash shell available. Unlike the server side dependency mentioned above, +this bash dependency is probably permament. + +*However*, you do not need to use this machine every day. Day to day +administration of gitolite (adding users, repos, etc) can be done from +anywhere; see next section. + +If you do not have such a machine, you have the following alternatives: + + * use a different userid on the same server + * use the same userid on the same server! + * (either of the above cases makes bash mandatory on the server, by the way) + * manually simulate the script directly on the server; doable, but tedious + +#### admin workstation(s) + +When you install gitolite, it creates a repository called "gitolite-admin" and +gives you permissions on it. + +Administering gitolite (adding repos/users, assigning permissions, etc) is +done by cloning this repo, making changes to a file called +`conf/gitolite.conf`, adding users' pubkeys to `keydir/`, and pushing the +changes back to the server. + +Which means all this can be done from *any* machine, even Windows. [Just make +sure your pubkey (from the Windows machine) has been added and permissions +given to allow you to push to the gitolite-admin repo. This is also how other +people can be added as "admins": add their pubkey to as +`keydir/.pub`, and give `` at least `RW` permissions on +the `gitolite-admin` repo in the `conf/gitolite.conf` file.] + From 0bdf1f360f240d524a76dd7fe01ef74c90959256 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 18 Nov 2009 14:54:18 +0530 Subject: [PATCH 142/850] all src/ and conf/: force crlf=input via gitattributes msysgit needs this on the initial clone, so it has to be on master. It doesn't seem to "apply" the gitattributes if you checkout a different branch later that has that setting; didn't investigate why --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5d894ad --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +conf/* crlf=input +src/* crlf=input +src/hooks/* crlf=input From 23be2b62406f0b0cfdd8b93999582285efa088c8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 18 Nov 2009 14:37:04 +0530 Subject: [PATCH 143/850] easy install (+doc): make it run from msys. Here's how: - all $HOME/blah becomes "$HOME/blah" (bl**dy "Documents and Settings" crap) - replace bash regex with perl, and in one case replace the check with something else - rsync changed to appropriate scp - since we no longer insist on running from a specific directory, create tmpgli dir *after* you cd to the right place --- doc/0-INSTALL.mkd | 35 +++++++++++++++-------------------- src/gl-easy-install | 40 ++++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 0ab8c97..b12edd2 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -1,5 +1,7 @@ # installing gitolite +[Update 2009-11-18: easy install now works from msysgit also!] + This document tells you how to install gitolite. After the install is done, you may want to see the [admin document][admin] for adding users, repos, etc. @@ -141,22 +143,18 @@ You need the following software on it: #### install workstation Installing or upgrading the gitolite software itself is best done by running -the easy-install program from a clone, usually on a different machine. +the easy-install program from a gitolite clone. -This script is heavily dependent on bash, so you need a unix machine that has -a bash shell available. Unlike the server side dependency mentioned above, -this bash dependency is probably permament. +This script is heavily dependent on bash, so you need a machine with a bash +shell. Even the bash that comes with msysgit is fine, if you don't have a +Linux box handy. -*However*, you do not need to use this machine every day. Day to day -administration of gitolite (adding users, repos, etc) can be done from -anywhere; see next section. +If you have neither Linux nor Windows+msysgit, you still have a few +alternatives: -If you do not have such a machine, you have the following alternatives: - - * use a different userid on the same server - * use the same userid on the same server! - * (either of the above cases makes bash mandatory on the server, by the way) - * manually simulate the script directly on the server; doable, but tedious + * use a different userid on the same server (assuming it has bash) + * use the same userid on the same server (same assumption) + * manually simulate the script directly on the server (doable, but tedious) #### admin workstation(s) @@ -168,10 +166,7 @@ done by cloning this repo, making changes to a file called `conf/gitolite.conf`, adding users' pubkeys to `keydir/`, and pushing the changes back to the server. -Which means all this can be done from *any* machine, even Windows. [Just make -sure your pubkey (from the Windows machine) has been added and permissions -given to allow you to push to the gitolite-admin repo. This is also how other -people can be added as "admins": add their pubkey to as -`keydir/.pub`, and give `` at least `RW` permissions on -the `gitolite-admin` repo in the `conf/gitolite.conf` file.] - +Which means all this can be done from *any* machine. You'll normally do it +from the same machine you used to install gitolite, but it doesn't have to be +the same one, as long as your pubkey has been added and permissions given to +allow you to push to the gitolite-admin repo. diff --git a/src/gl-easy-install b/src/gl-easy-install index e5d23da..99e0d8a 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -33,10 +33,10 @@ fi # ---------------------------------------------------------------------- main() { - setup_tempdir - basic_sanity "$@" + setup_tempdir + version_info "$@" [[ -n $admin_name ]] && setup_local_ssh @@ -140,11 +140,9 @@ basic_sanity() { # MANUAL: this *must* be run as "src/gl-easy-install", not by cd-ing to # src and then running "./gl-easy-install" - [[ $0 =~ ^src/gl-easy-install$ ]] || - { - die "please cd to the gitolite repo top level directory and run this as - 'src/gl-easy-install'" - } + bindir=${0%/*} + # switch to parent of bindir; we assume the conf files are all there + cd $bindir; cd .. # are we in quiet mode? quiet= @@ -163,14 +161,16 @@ basic_sanity() { admin_name=$3 # but if the 3rd arg is a number, that's a port number, and the 4th arg is # the admin_name - if [[ $3 =~ ^[0-9]+$ ]] + if echo $3 | perl -lne 'exit 1 unless /^[0-9]+$/' then port=$3 admin_name=$4 fi - [[ "$user" =~ [^a-zA-Z0-9._-] ]] && die "user '$user' invalid" - [[ -n $admin_name ]] && [[ "$admin_name" =~ [^a-zA-Z0-9._-] ]] && die "admin_name '$admin_name' invalid" + echo $user | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' || + die "user '$user' invalid" + echo $admin_name | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' || + die "admin_name '$admin_name' invalid" # MANUAL: make sure you're in the gitolite directory, at the top level. # The following files should all be visible: @@ -225,11 +225,11 @@ setup_local_ssh() { prompt "setting up keypair..." "$v_setting_up_keypair" - if [[ -f $HOME/.ssh/$admin_name.pub ]] + if [[ -f "$HOME/.ssh/$admin_name.pub" ]] then prompt " ...reusing $HOME/.ssh/$admin_name.pub..." "$v_reuse_pubkey" else - ssh-keygen -t rsa -f $HOME/.ssh/$admin_name || die "ssh-keygen failed for some reason..." + ssh-keygen -t rsa -f "$HOME/.ssh/$admin_name" || die "ssh-keygen failed for some reason..." fi # MANUAL: copy the pubkey created to the server, say to /tmp. This would @@ -245,7 +245,7 @@ setup_local_ssh() { if ssh-add -l &>/dev/null then prompt " ...adding key to agent..." "$v_ssh_add" - ssh-add $HOME/.ssh/$admin_name + ssh-add "$HOME/.ssh/$admin_name" fi # MANUAL: you now need to add some lines to the end of your ~/.ssh/config @@ -266,14 +266,14 @@ setup_local_ssh() { port $port identityfile ~/.ssh/$admin_name" > $tmpgli/.gl-stanza - if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null + if grep 'host *gitolite' "$HOME/.ssh/config" &>/dev/null then prompt "found gitolite para in ~/.ssh/config; assuming it is correct..." "$v_found_para" else prompt "creating gitolite para in ~/.ssh/config..." "$v_creating_para" - cat $tmpgli/.gl-stanza >> $HOME/.ssh/config + cat $tmpgli/.gl-stanza >> "$HOME/.ssh/config" # if the file didn't exist at all, it might have the wrong permissions - chmod 644 $HOME/.ssh/config + chmod 644 "$HOME/.ssh/config" fi } @@ -288,7 +288,7 @@ copy_gl() { # have to create the directory first. ssh -p $port $user@$host mkdir -p gitolite-install - rsync $quiet -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/ + scp $quiet -P $port -r src conf doc $user@$host:gitolite-install/ rm -f src/VERSION # MANUAL: now log on to the server (ssh git@server) and get a command @@ -384,7 +384,7 @@ repo testing # send the config and the key to the remote scp $quiet -P $port $tmpgli/gitolite.conf $user@$host:$GL_ADMINDIR/conf/ - scp $quiet -P $port $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir + scp $quiet -P $port "$HOME/.ssh/$admin_name.pub" $user@$host:$GL_ADMINDIR/keydir # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" @@ -426,7 +426,7 @@ GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty prompt "cloning gitolite-admin repo..." "$v_cloning" cleanup - cd $HOME + cd "$HOME" git clone gitolite:gitolite-admin.git # MANUAL: be sure to read the message below; this applies to you too... @@ -464,7 +464,7 @@ successfully cloned and pushed the gitolite-admin repo. After that, install 'keychain' or something similar, and add the following command to your bashrc (since this is a non-default key) - ssh-add \\\$HOME/.ssh/\$admin_name + ssh-add "\\\$HOME/.ssh/\$admin_name" This makes using passphrases very convenient. " From d332b1537dcf246117d0687215d2e4a38cd9def1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 19 Nov 2009 17:38:40 +0530 Subject: [PATCH 144/850] serverside install/ga-hook: solaris compat --- src/ga-post-update-hook | 4 ++-- src/gl-install | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ga-post-update-hook b/src/ga-post-update-hook index 2613340..8090b8a 100755 --- a/src/ga-post-update-hook +++ b/src/ga-post-update-hook @@ -2,7 +2,7 @@ # get this from your .gitolite.conf; and don't forget this is shell, while # that is perl :-) -export GL_ADMINDIR=$HOME/.gitolite +export GL_ADMINDIR; GL_ADMINDIR=$HOME/.gitolite # checkout the master branch to $GL_ADMINDIR GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master @@ -13,7 +13,7 @@ GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master rm -rf $GL_ADMINDIR/conf/fragments # collect all the delegated fragments mkdir $GL_ADMINDIR/conf/fragments -for br in $(git for-each-ref --format='%(refname:short)') +for br in `git for-each-ref --format='%(refname:short)'` do # skip master (duh!) [ "$br" = "master" ] && continue diff --git a/src/gl-install b/src/gl-install index afea0dc..a4dc729 100755 --- a/src/gl-install +++ b/src/gl-install @@ -78,8 +78,8 @@ for my $repo (`find . -type d -name "*.git"`) { # oh and one of those repos is a bit more special and has an extra hook :) if ( -d "gitolite-admin.git/hooks" ) { print "copying post-update hook to gitolite-admin repo...\n"; - system("cp -v $GL_ADMINDIR/src/ga-post-update-hook gitolite-admin.git/hooks/post-update"); - system("perl", "-i", "-p", "-e", "s(export GL_ADMINDIR=.*)(export GL_ADMINDIR=$GL_ADMINDIR)", + system("cp $GL_ADMINDIR/src/ga-post-update-hook gitolite-admin.git/hooks/post-update"); + system("perl", "-i", "-p", "-e", "s(GL_ADMINDIR=.*)(GL_ADMINDIR=$GL_ADMINDIR)", "gitolite-admin.git/hooks/post-update"); chmod 0755, "gitolite-admin.git/hooks/post-update"; } From cba66c6e5a56043c1a747104eb49b1356df5716c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 23 Nov 2009 18:04:18 +0530 Subject: [PATCH 145/850] compile: make compiled config be key-sorted makes debugging access changes much easier (doh! why didn't I do this earlier!) --- src/gl-compile-conf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index de3cf00..247e5cc 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -4,6 +4,7 @@ use strict; use warnings; use Data::Dumper; $Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; # === add-auth-keys === From 516c028b815fd54231bfac962b919ab5af600835 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 23 Nov 2009 22:45:00 +0530 Subject: [PATCH 146/850] compile: (oopsies...) plug security hole in delegation feature I was trying to determine how close gitolite can come to the ACL model of a proprietary product called codebeamer, and one of the items was how to make a "role" (like QA_Lead) have different "members" in different projects. I then realised delegation already does that! Which is great, but as I thought about it more, I realised... well, we'll let the in-code comments speak for themselves :-) Anyway, all it needed was a 1-line fix, luckily... And it would have only affected people who use delegation. --- src/gl-compile-conf | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 247e5cc..77d340b 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -86,7 +86,7 @@ my $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple patter # $groups{group}{member} = "master" (or name of fragment file in which the # group is defined). -my %groups = (); +our %groups = (); # %repos has two functions. @@ -282,6 +282,24 @@ parse_conf_file($GL_CONF, 'master'); wrap_chdir($GL_ADMINDIR); for my $fragment_file (glob("conf/fragments/*.conf")) { + # we already check (elsewhere) that a fragment called "foo" will not try + # to specify access control for a repo whose name is not "foo" or is not + # part of a group called "foo" created by master + + # meanwhile, I found a possible attack where the admin for group B creates + # a "convenience" group of (a subset of) his users, and then the admin for + # repo group A (alphabetically before B) adds himself to that same group + # in his own fragment. + + # as a result, admin_A now has access to group B repos :( + + # so now we lock the groups hash to the value it had after parsing + # "master", and localise any changes to it by this fragment so that they + # don't propagate to the next fragment. Thus, each fragment now has only + # those groups that are defined in "master" and itself + + local %groups = %groups; + my $fragment = $fragment_file; $fragment =~ s/^conf\/fragments\/(.*).conf$/$1/; parse_conf_file($fragment_file, $fragment); From de2e38c3726a4cbc41243d187b0210e3a8066761 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 22 Nov 2009 10:21:22 +0530 Subject: [PATCH 147/850] minor doc/message updates/clarifications --- doc/0-INSTALL.mkd | 12 +++--------- doc/6-ssh-troubleshooting.mkd | 2 ++ src/gl-auth-command | 2 +- src/gl-easy-install | 10 ++++------ 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index b12edd2..106e415 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -28,9 +28,8 @@ In this document: ### easy install There is an easy install script that makes installing very easy for the common -case. **This script is meant to be run on your workstation, not on the -server!** It will take care of all the server side work, *and* get you -"push-to-admin" too :-) In short, it does **everything**! +case. **This script will setup everything on the server, but you have to run +it on your workstation, NOT on the server!** Assumptions/pre-requisites: @@ -120,7 +119,7 @@ gitolite. This is where gitolite is eventually installed. You need a *normal* userid (typically "git" but can be anything) on this machine; root access is *not* -needed. +needed, but it has to be some sort of Unix (not Windows). You need the following software on it: @@ -134,11 +133,6 @@ You need the following software on it: * openssh server * (I guess any ssh server that can understand the `authorized_keys` file format should work) - * **bash shell** - * a small part of the gitolite server side is written in "bash"; I - intend to test it (time permitting) on non-bash (like ksh or plain sh) - on non-Linux servers. Once that is done, this bash dependency will go - away. #### install workstation diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 462b59a..29459ba 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -137,6 +137,8 @@ Here's how it all hangs together. the `gl-auth-command` program at all, and so none of gitolite's access control will work. + + You need to force ssh to use the *other* keypair when performing a git operation. With normal ssh, that would be diff --git a/src/gl-auth-command b/src/gl-auth-command index f516657..5da4878 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -96,7 +96,7 @@ die "bad command: $cmd. Make sure the repo name is exactly as in your config\n" # we know the user and repo; we just need to know what perm he's trying my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W'); -die "$perm access for $repo denied to $user\n" +die "$perm access for $repo DENIED to $user\n" unless $repos{$repo}{$perm}{$user} or $repos{$repo}{$perm}{'@all'}; diff --git a/src/gl-easy-install b/src/gl-easy-install index 99e0d8a..e3616ef 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -119,8 +119,6 @@ Notes: gitolite admin Pre-requisites: - - you must run this from the gitolite working tree top level directory. - This means you run this as "src/gl-easy-install" - you must already have pubkey based access to user@host. If you currently only have password access, use "ssh-copy-id" or something equivalent (or copy the key manually). Somehow (doesn't matter how), get to the point @@ -420,14 +418,14 @@ GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty # MANUAL: you're done! Log out of the server, come back to your # workstation, and clone the admin repo using "git clone - # gitolite:gitolite-admin.git", or pull once again if you already have a + # gitolite:gitolite-admin", or pull once again if you already have a # clone prompt "cloning gitolite-admin repo..." "$v_cloning" cleanup cd "$HOME" - git clone gitolite:gitolite-admin.git + git clone gitolite:gitolite-admin # MANUAL: be sure to read the message below; this applies to you too... @@ -577,9 +575,9 @@ keychain or some such software, you may have to run this each time you log in: URLS: *Your* URL for cloning any repo on this server will be - gitolite:reponame.git + gitolite:reponame *Other* users you set up will have to use - \$user@\$host:reponame.git + \$user@\$host:reponame " From ad6d46fab908bc4f08ad1432597265536bdb35b2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 22 Nov 2009 10:42:32 +0530 Subject: [PATCH 148/850] easy install: when the first install doesn't go right... We detect an upgrade situation by the presence of $GL_ADMINDIR/conf/gitolite.conf -- if it exists, we reason, this is not a fresh install. And if so we skip setting up PTA, and the initial clone. Well, turns out this is not always true. I've had a few cases where the first install didn't go right, but left enough stuff in to make the subsequent attempt think this is an upgrade. [This mostly happened to me when I was testing the "oldgits" branch, and also when I was making it work from msysgit I think... regardless of why, it'd be good to fix] So this changes the flow somewhat. Now the *only* difference between a fresh install and an ugrade is the "initial_conf_key" function call (you don't want to overwrite an existing conf file or keydir!) --- src/gl-easy-install | 48 ++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index e3616ef..6a51dfa 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -45,19 +45,14 @@ main() { run_install - [[ $upgrade == 1 ]] && { - # just compile it, in case the config file's internal format has - # changed and the hooks expect something different - ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" + [[ $upgrade == 0 ]] && initial_conf_key - eval "echo \"$v_done\"" - cleanup - exit 0 - } - - initial_conf_key + # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" + ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" setup_pta + + clone_it } # ---------------------------------------------------------------------- @@ -140,7 +135,7 @@ basic_sanity() { bindir=${0%/*} # switch to parent of bindir; we assume the conf files are all there - cd $bindir; cd .. + cd "$bindir"; cd .. # are we in quiet mode? quiet= @@ -383,9 +378,6 @@ repo testing # send the config and the key to the remote scp $quiet -P $port $tmpgli/gitolite.conf $user@$host:$GL_ADMINDIR/conf/ scp $quiet -P $port "$HOME/.ssh/$admin_name.pub" $user@$host:$GL_ADMINDIR/keydir - - # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" - ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" } # ---------------------------------------------------------------------- @@ -407,7 +399,7 @@ setup_pta() { echo "cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir -GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty +GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start " | ssh -p $port $user@$host # MANUAL: now that the admin repo is created, you have to set the hooks @@ -420,19 +412,26 @@ GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty # workstation, and clone the admin repo using "git clone # gitolite:gitolite-admin", or pull once again if you already have a # clone +} - prompt "cloning gitolite-admin repo..." "$v_cloning" - +clone_it() +{ cleanup cd "$HOME" - git clone gitolite:gitolite-admin + if [[ -d gitolite-admin ]] + then + echo $HOME/gitolite-admin exists, skipping clone step... + else + prompt "cloning gitolite-admin repo..." "$v_cloning" + git clone gitolite:gitolite-admin + fi # MANUAL: be sure to read the message below; this applies to you too... echo echo echo --------------------------------------------------------------- - eval "echo \"$tail\"" + eval "echo \"$v_done\"" } # ---------------------------------------------------------------------- @@ -537,10 +536,15 @@ install, not this one... v_done=" done! -If you forgot the help message you saw when you first ran this, there's a -somewhat generic version of it at the end of this file. Try: +Reminder: + *Your* URL for cloning any repo on this server will be + gitolite:reponame.git + *Other* users you set up will have to use + \$user@\$host:reponame.git - tail -31 \$0 + If this is your first time installing gitolite, please also: + tail -31 \$0 + for next steps. " v_cloning=" From 1c786d880a4c813139701fabbaeaf044f6077ed8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 25 Nov 2009 08:46:21 +0530 Subject: [PATCH 149/850] doc/3: $repo_base -> $projectroot; honor @all in perm check - it appears that what we call $repo_base, gitweb already needs as $projectroot - allow read of repos defined as readable by @all plus some minor declaration changes to make the sample code work as is (thanks to teemu dot matilainen at iki dot fi) --- doc/3-faq-tips-etc.mkd | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 05eb8b7..5dcd935 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -267,11 +267,13 @@ already done and we just use it! # completely untested... but the basic idea should work fine # change these as needed - $repo_base = '/home/git/repositories/'; - $gl_conf_compiled = '/home/git/.gitolite/conf/gitolite.conf-compiled.pm'; + # projectroot should be the same as gitolite's REPO_BASE, but converted to + # an absolute path + $projectroot = '/home/git/repositories/'; + my $gl_conf_compiled = '/home/git/.gitolite/conf/gitolite.conf-compiled.pm'; # I assume this gives us the HTTP auth username - $username = $cgi->remote_user; + my $username = $cgi->remote_user; # ---------- @@ -284,12 +286,13 @@ already done and we just use it! $export_auth_hook = sub { my $reponame = shift; # gitweb passes us the full repo path; so we strip the beginning... - $reponame =~ s/\Q$repo_base//; + $reponame =~ s/\Q$projectroot//; # ...and the end, to get the repo name as it is specified in gitolite conf $reponame =~ s/\.git$//; - return exists $repos{$reponame}{R}{$username}; - } + return exists $repos{$reponame}{R}{$username} + || exists $repos{$reponame}{R}{'@all'}; + }; [leho]: http://leho.kraav.com/news/2009/10/27/using-apache-authentication-with-gitweb-gitosis-repository-access-control/ From 9a85f5d0d6dea071bf8322069325379338d12f92 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 25 Nov 2009 09:15:36 +0530 Subject: [PATCH 150/850] auth: print permissions for @all also I don't have a use for "@all" at all (pun not intended!) other than the "testing" repo, but sent in a patch to mark those repos with "R" and "W" in the permissions list, and I started thinking about it. This could actually be useful if we *differentiated* such access from normal (explicit username) access. From the "corporate environment" angle, it would be nice if a project manager could quickly check if any of his projects have erroneously been made accessible by @all. So what we do now is print "@" in the corresponding column if "@all" has the corresponding access. Also, when someone has access both as himself *and* via @all, we print the "@"; printing the "R" or "W" would hide the "@", and wouldn't correctly satisfy the use case described above. --- doc/3-faq-tips-etc.mkd | 1 + src/gl-auth-command | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 5dcd935..6147e95 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -389,6 +389,7 @@ shell: R W gitolite-admin R W indic_web_input R W proxy + @ @ testing R W vkc Note that until this version, we used to put out an ugly `need diff --git a/src/gl-auth-command b/src/gl-auth-command index 5da4878..31d1fa8 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -67,8 +67,8 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { system("cat", "$GL_ADMINDIR/src/VERSION"); print "\ryou have the following permissions:\n\r"; for my $r (sort keys %repos) { - my $perm .= " R" if $repos{$r}{R}{$user}; - $perm .= " W" if $repos{$r}{W}{$user}; + my $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{$r}{R}{$user} ? ' R' : '' ) ); + $perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{$r}{W}{$user} ? ' W' : '' ) ); print "$perm\t$r\n\r" if $perm; } exit 1; From a02a48e8f5439a998554db0cf070c8d89b458766 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 26 Nov 2009 12:13:42 +0530 Subject: [PATCH 151/850] easy install: dont allow root, plus warn about shell access using the given key - refuse to install to root - when a pubkey is being used that was not freshly created by ourselves, warn the user that this key can not be used to get shell access to the server. Prevents some corner cases of people being locked out... Also, change the final message to be even more clear that this is all on the workstation, not the server --- doc/6-ssh-troubleshooting.mkd | 4 ++-- src/gl-easy-install | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 29459ba..2b9febc 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -91,7 +91,7 @@ Here's how it all hangs together. 400 characters; I snipped 'em in the middle, as you can see. In contrast, pubkey lines that give access to git repos hosted by gitolite - looks like this: + look like this: command="[some path]src/gl-auth-command sitaram",[some restrictions] ssh-rsa AAAAB3NzaC[snip]s18OnB42oQ== sitaram@sita-lt @@ -146,7 +146,7 @@ Here's how it all hangs together. but git does not support putting an alternate keypair in the URL. - Luckily, ssh has a very convenient way of capturing all the mundane + Luckily, ssh has a very convenient way of capturing all the connection information (username, hostname, port number (if it's not the default 22), and keypair to be used) in one "paragraph". This is what the para looks like for us (the easy install script puts it there the first time): diff --git a/src/gl-easy-install b/src/gl-easy-install index 6a51dfa..c577307 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -162,6 +162,7 @@ basic_sanity() { echo $user | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' || die "user '$user' invalid" + [[ "$user" == "root" ]] && die I refuse to install to root echo $admin_name | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' || die "admin_name '$admin_name' invalid" @@ -220,7 +221,7 @@ setup_local_ssh() { if [[ -f "$HOME/.ssh/$admin_name.pub" ]] then - prompt " ...reusing $HOME/.ssh/$admin_name.pub..." "$v_reuse_pubkey" + prompt "" "$v_reuse_pubkey" else ssh-keygen -t rsa -f "$HOME/.ssh/$admin_name" || die "ssh-keygen failed for some reason..." fi @@ -467,8 +468,14 @@ This makes using passphrases very convenient. " v_reuse_pubkey=" -Hmmm... pubkey \$HOME/.ssh/\$admin_name.pub exists; should I just re-use it? -Be sure you remember the passphrase, if you gave one when you created it! +Hmmm... pubkey \$HOME/.ssh/\$admin_name.pub exists; should I just (re-)use it? + +IMPORTANT: once the install completes, *this* key can no longer be used to get +a command line on the server -- it will be used by gitolite, for git access +only. If that is a problem, please ABORT now. + +doc/6-ssh-troubleshooting.mkd will explain what is happening here, if you need +more info. " v_ssh_add=" @@ -554,16 +561,16 @@ it elsewhere later if you wish to. " tail=" -All done! +NOTE: All the below stuff is on your *workstation*. You shoud not, normally, +have to do anything directly on your server to administer/use gitolite. -The admin repo is currently cloned at ~/gitolite-admin; you can clone it -anywhere you like. To administer gitolite, make changes to the config file -(config/gitolite.conf) and/or the pubkeys (in subdirectory 'keydir') in any +The admin repo is currently cloned at ~/gitolite-admin. You can reclone it +anywhere else if you wish. To administer gitolite, make changes to the config +file (conf/gitolite.conf) and/or the pubkeys (in subdirectory 'keydir') in any clone, then git add, git commit, and git push. ADDING REPOS: Edit the config file to give *some* user access to the repo. -When you push, an empty repo will be created on the server, which authorised -users can then clone from, or push to. +When you push, an empty repo will be created on the server. ADDING USERS: copy their pubkey as keydir/.pub, add it, commit and push. From 6e0855eb4d4050a11d1fd7d3539e52e07139dba4 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 26 Nov 2009 19:30:37 +0530 Subject: [PATCH 152/850] compile: gitweb/daemon writes are unconditional now writing the export_ok files and the gitweb project list are now unconditional. They're idempotent anyway, and I doubt anyone cared about all the fancy logic to detect and report *just* the new ones on each compile. This paves the way for gitweb ownership to be added later; that code was becoming too complex otherwise... --- src/gl-compile-conf | 48 ++++++++------------------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 77d340b..69b2ccb 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -360,67 +360,35 @@ warn "\n\t\t***** WARNING *****\n" . wrap_chdir("$repo_base_abs"); -# get the current project list; note that the file may not yet exist if no -# gitweb access has been specified so far -my %projlist = (); -if (-f $PROJECTS_LIST) { - my $projlist_fh = wrap_open( "<", $PROJECTS_LIST); - while(<$projlist_fh>) { - chomp; - $projlist{$_} = 1; - } - close $projlist_fh; -} -my $projlist_changed = 0; - # daemons first... for my $repo (sort keys %repos) { my $export_ok = "$repo.git/git-daemon-export-ok"; if ($repos{$repo}{'R'}{'daemon'}) { - unless (-f $export_ok) { - system("touch $export_ok"); - print "daemon add $repo.git\n"; - } + system("touch $export_ok"); } else { - if (-f $export_ok) { - unlink($export_ok); - print "daemon del $repo.git\n"; - } + unlink($export_ok); } } +my %projlist = (); # ...then gitwebs for my $repo (sort keys %repos) { my $desc_file = "$repo.git/description"; # note: having a description also counts as enabling gitweb if ($repos{$repo}{'R'}{'gitweb'} or $desc{$repo}) { - unless ($projlist{"$repo.git"}) { - # not in the old list; add it to the new one - $projlist{"$repo.git"} = 1; - $projlist_changed = 1; - print "gitweb add $repo.git\n"; - } + $projlist{"$repo.git"} = 1; # add the description file; no messages to user or error checking :) $desc{$repo} and open(DESC, ">", $desc_file) and print DESC "$desc{$repo}\n" and close DESC; } else { - if ($projlist{"$repo.git"}) { - # delete it from new list - delete $projlist{"$repo.git"}; - $projlist_changed = 1; - print "gitweb del $repo.git\n"; - } # delete the description file; no messages to user or error checking :) unlink $desc_file; } } -# has there been a change in the gitweb projects list? -if ($projlist_changed) { - print "updating gitweb project list $PROJECTS_LIST\n"; - my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); - print $projlist_fh join("\n", sort keys %projlist), "\n" if %projlist; - close $projlist_fh; -} +# update the project list +my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); +print $projlist_fh join("\n", sort keys %projlist), "\n" if %projlist; +close $projlist_fh; # ---------------------------------------------------------------------------- # "compile" ssh authorized_keys From d2a053ba3c4384059922ae6821dcdef1bb6f1cb8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 27 Nov 2009 13:23:48 +0530 Subject: [PATCH 153/850] compile: add owner field in the same line as the gitweb descriptions this goes into the project list --- src/gl-compile-conf | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 69b2ccb..6ca9b9b 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -107,8 +107,9 @@ my %rurp_seen = (); # catch usernames<->pubkeys mismatches; search for "lint" below my %user_list = (); -# gitweb descriptions, plain text, keyed by repo +# gitweb descriptions and owners; plain text, keyed by "$repo.git" my %desc = (); +my %owner = (); # set the umask before creating any files umask($REPO_UMASK); @@ -253,14 +254,18 @@ sub parse_conf_file } } } - # very simple syntax for the gitweb description of repo - elsif (/^(\S+) = "(.*)"$/) + # very simple syntax for the gitweb description of repo; one of: + # reponame = "some description string" + # reponame "owner name" = "some description string" + elsif (/^(\S+)(?: "(.*?)")? = "(.*)"$/) { - my ($repo, $desc) = ($1, $2); + my ($repo, $owner, $desc) = ($1, $2, $3); die "$ABRT bad repo name $repo\n" unless $repo =~ $REPONAME_PATT; die "$WARN $fragment attempting to set description for $repo\n" if $fragment ne 'master' and $fragment ne $repo and ($groups{"\@$fragment"}{$repo} || '') ne 'master'; - $desc{$repo} = $desc; + $desc{"$repo.git"} = $desc; + $owner =~ s/ /+/g if $owner; # gitweb/INSTALL wants more, but meh...! + $owner{"$repo.git"} = $owner || ''; } else { @@ -375,10 +380,10 @@ my %projlist = (); for my $repo (sort keys %repos) { my $desc_file = "$repo.git/description"; # note: having a description also counts as enabling gitweb - if ($repos{$repo}{'R'}{'gitweb'} or $desc{$repo}) { + if ($repos{$repo}{'R'}{'gitweb'} or $desc{"$repo.git"}) { $projlist{"$repo.git"} = 1; # add the description file; no messages to user or error checking :) - $desc{$repo} and open(DESC, ">", $desc_file) and print DESC "$desc{$repo}\n" and close DESC; + $desc{"$repo.git"} and open(DESC, ">", $desc_file) and print DESC $desc{"$repo.git"} . "\n" and close DESC; } else { # delete the description file; no messages to user or error checking :) unlink $desc_file; @@ -387,7 +392,9 @@ for my $repo (sort keys %repos) { # update the project list my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); -print $projlist_fh join("\n", sort keys %projlist), "\n" if %projlist; +for my $proj (sort keys %projlist) { + print $projlist_fh "$proj" . ( $owner{$proj} ? " $owner{$proj}" : "" ) . "\n"; +} close $projlist_fh; # ---------------------------------------------------------------------------- From d8cb62934fc391a4a8dc8e51679a2866d3f50877 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 27 Nov 2009 13:47:21 +0530 Subject: [PATCH 154/850] docs: document how to specify "owner" for gitweb --- README.mkd | 2 +- conf/example.conf | 8 ++++---- doc/2-admin.mkd | 11 ++++++++--- doc/3-faq-tips-etc.mkd | 4 ++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/README.mkd b/README.mkd index e1a9616..904d9f3 100644 --- a/README.mkd +++ b/README.mkd @@ -73,7 +73,7 @@ detail [here][gsdiff]. * config file syntax gets checked upfront, and much more thoroughly * if your requirements are still too complex, you can split up the config file and delegate authority over parts of it - * easier to specify gitweb "description" and gitweb/daemon access + * easier to specify gitweb owner, description and gitweb/daemon access * easier to sync gitweb (http) authorisation with gitolite's access config * more comprehensive logging [aka: management does not think "blame" is just a synonym for "annotate" :-)] diff --git a/conf/example.conf b/conf/example.conf index 752c279..e00ec25 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -130,13 +130,13 @@ repo @oss_repos repo linux perl R = gitweb -# GITWEB DESCRIPTION LINE +# REPO OWNER/DESCRIPTION LINE FOR GITWEB -# syntax: +# syntax, one of: # reponame = "some description string in double quotes" +# reponame "owner name" = "some description string in double quotes" # note: setting a description also gives gitweb access; you do not have to # give gitweb access as described above if you're specifying a description -gitolite = "fast, secure, access control for git in a corporate environment" - +gitolite "Sitaram Chamarty" = "fast, secure, access control for git in a corporate environment" diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 7038fb9..d0460ec 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -57,13 +57,18 @@ so you may as well use that method and kill two birds with one stone, like so: gitolite = "fast, secure, access control for git in a corporate environment" +You can also specify an owner for gitweb to show, if you like: + + gitolite "Sitaram Chamarty" = "fast, secure, access control for git in a corporate environment" + Note that gitolite does **not** install or configure gitweb/daemon -- that is a one-time setup you must do separately. All this does is: * for daemon, create the file `git-daemon-export-ok` in the repository - * for gitweb, add the repo to the list of projects to be served by gitweb - (see the config file variable `$PROJECTS_LIST`, which should have the same - value you specified for `$projects_list` when setting up gitweb) + * for gitweb, add the repo (plus owner name, if given) to the list of + projects to be served by gitweb (see the config file variable + `$PROJECTS_LIST`, which should have the same value you specified for + `$projects_list` when setting up gitweb) * put the description, if given, in `$repo/description` The "compile" script will keep these files consistent with the config settings diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 6147e95..7450994 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -204,6 +204,10 @@ file: reponame = "one line of description" +You can also specify an "owner": + + reponame "owner name" = "one line of description" + To enable access to one or more repos via git daemon, just give "read" permissions to the special username `daemon`. From c3b5e3b1af5cca572a044da412e6822f968090a1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 26 Nov 2009 21:30:59 +0530 Subject: [PATCH 155/850] compile, pm: factor out new repo creation ...also wrap_chdir, wrap_open, $ABRT, and $WARN --- src/gitolite.pm | 33 +++++++++++++++++++++++++++++++-- src/gl-compile-conf | 28 ++++------------------------ 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 2c325e2..c7276bd 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -9,8 +9,9 @@ # the name of this file will change as soon as its function/feature set # stabilises enough ;-) -# right now all it does is define a function that tells you where to find the -# rc file +# right now all it does is +# - define a function that tells you where to find the rc file +# - define a function that creates a new repo and give it our update hook # ---------------------------------------------------------------------------- # where is the rc file hiding? @@ -42,4 +43,32 @@ sub where_is_rc } } +$ABRT = "\n\t\t***** ABORTING *****\n "; +$WARN = "\n\t\t***** WARNING *****\n "; +sub wrap_chdir { + chdir($_[0]) or die "$ABRT chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; +} + +sub wrap_open { + open (my $fh, $_[0], $_[1]) or die "$ABRT open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n" . + ( $_[2] || '' ); # suffix custom error message if given + return $fh; +} + +# NOTE: this sub will change your cwd; caller beware! +sub new_repo +{ + my ($repo, $hooks_dir) = @_; + + umask($REPO_UMASK); + + system("mkdir", "-p", "$repo.git") and die "$ABRT mkdir $repo.git failed: $!\n"; + # erm, note that's "and die" not "or die" as is normal in perl + wrap_chdir("$repo.git"); + system("git --bare init >&2"); + # propagate our own, plus any local admin-defined, hooks + system("cp $hooks_dir/* hooks/"); + chmod 0755, "hooks/update"; +} + 1; diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 6ca9b9b..d939e5f 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,12 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH); - -# now that this thing *may* be run via "push to admin", any errors have to -# grab the admin's ATTENTION so he won't miss them among the other messages a -# typical push generates -my $ABRT = "\n\t\t***** ABORTING *****\n "; -my $WARN = "\n\t\t***** WARNING *****\n "; +our ($ABRT, $WARN); # the common setup module is in the same directory as this running program is my $bindir = $0; @@ -118,16 +113,6 @@ umask($REPO_UMASK); # subroutines # ---------------------------------------------------------------------------- -sub wrap_chdir { - chdir($_[0]) or die "$ABRT chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; -} - -sub wrap_open { - open (my $fh, $_[0], $_[1]) or die "$ABRT open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n" . - ( $_[2] || '' ); # suffix custom error message if given - return $fh; -} - sub expand_list { my @list = @_; @@ -337,15 +322,10 @@ for my $repo (keys %repos) { unless (-d "$repo.git") { - system("mkdir", "-p", "$repo.git") and die "$ABRT mkdir $repo.git failed: $!\n"; - # erm, note that's "and die" not "or die" as is normal in perl - wrap_chdir("$repo.git"); - system("git --bare init"); - # propagate our own, plus any local admin-defined, hooks - system("cp $GL_ADMINDIR/src/hooks/* hooks/"); - chmod 0755, "hooks/update"; - wrap_chdir("$repo_base_abs"); + new_repo($repo, "$GL_ADMINDIR/src/hooks"); $git_too_old++ if $git_version < 10602; # that's 1.6.2 to you + # new_repo would have chdir'd us away; come back + wrap_chdir("$repo_base_abs"); } } warn "\n\t\t***** WARNING *****\n" . From b78a720ceeecf7db985dd8d013c08829ea8cfc81 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 27 Nov 2009 23:00:58 +0530 Subject: [PATCH 156/850] auth/compile: auto-vivify is default now, so: the "create a new repo" code moves from compile to auth. Only someone who has W access can create it, but he can do so even on a "R" operation (like clone or ls-remote). This is a pre-requisite for rebel's wildcard repos, where autovivification is the only way you can create arbitrary repos matching a pattern. The only reason it's getting into master is because it looks cool! ---- OK that's a lie; the real reason is to keep the two branches as similar as possible, though they;ve diverged quite a bit since the "only one-line difference" days where "rebel" just meant "deny/exclude" rules!) --- src/gl-auth-command | 9 +++++++++ src/gl-compile-conf | 21 +++++---------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 31d1fa8..a135be6 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -100,6 +100,15 @@ die "$perm access for $repo DENIED to $user\n" unless $repos{$repo}{$perm}{$user} or $repos{$repo}{$perm}{'@all'}; +# create the repo if it doesn't already exist and the user has "W" access +my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +if ( ( $repos{$repo}{W}{$user} + or $repos{$repo}{W}{'@all'} ) and not -d "$repo_base_abs/$repo.git" ) { + wrap_chdir("$repo_base_abs"); + new_repo($repo, "$GL_ADMINDIR/src/hooks"); + wrap_chdir($ENV{HOME}); +} + # ---------------------------------------------------------------------------- # logging, timestamp. also setup env vars for later # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index d939e5f..583a3f0 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -312,27 +312,12 @@ my $git_version = `git --version`; my ($gv_maj, $gv_min, $gv_patchrel) = ($git_version =~ m/git version (\d+)\.(\d+)\.(\d+)/); die "$ABRT I can't understand $git_version\n" unless ($gv_maj >= 1); $git_version = $gv_maj*10000 + $gv_min*100 + $gv_patchrel; # now it's "normalised" -my $git_too_old = 0; -# repo-base needs to be an absolute path for this loop to work right -# so if it was not already absolute, prefix $HOME. -my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); -wrap_chdir("$repo_base_abs"); -for my $repo (keys %repos) -{ - unless (-d "$repo.git") - { - new_repo($repo, "$GL_ADMINDIR/src/hooks"); - $git_too_old++ if $git_version < 10602; # that's 1.6.2 to you - # new_repo would have chdir'd us away; come back - wrap_chdir("$repo_base_abs"); - } -} warn "\n\t\t***** WARNING *****\n" . "\tyour git version is older than 1.6.2\n" . "\tgitolite will work but you MUST read the section on\n" . "\t\"git version dependency\" in doc/3-faq-tips-etc.mkd\n" - if $git_too_old; + if $git_version < 10602; # that's 1.6.2 to you # ---------------------------------------------------------------------------- # handle gitweb and daemon @@ -343,6 +328,10 @@ warn "\n\t\t***** WARNING *****\n" . # :-) These are now "pseduo users" -- giving them "R" access to a repo is all # you have to do +# repo-base needs to be an absolute path for this loop to work right +# so if it was not already absolute, prefix $HOME. +my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); + wrap_chdir("$repo_base_abs"); # daemons first... From 6576e82e3342a869e18845df432ef4128e693131 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 27 Nov 2009 23:53:38 +0530 Subject: [PATCH 157/850] easy install: needs a minor fix to accommodate auto-vivification --- src/gl-easy-install | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gl-easy-install b/src/gl-easy-install index c577307..cf3a859 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -398,6 +398,7 @@ setup_pta() { # Substitute $GL_ADMINDIR and $REPO_BASE appropriately. Note there is no # space around the "=" in the second and third lines. + git ls-remote gitolite:gitolite-admin echo "cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start From bfc3b6cd58510cd4d1bc8b538cc3d320248ccabb Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 30 Nov 2009 07:00:31 +0530 Subject: [PATCH 158/850] example conf: clarify group name parsing --- conf/example.conf | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index e00ec25..6bdc834 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -24,9 +24,13 @@ # syntax: # @groupname = [one or more names] -# groups let you club names together for convenience in specifying -# permissions. A group is simply expanded to whatever names are on the right -# hand side when it is actually used +# groups let you club (user or group) names together for convenience + +# * a group is like a #define in C except that it can *accumulate* values +# * the config file is parsed in a single-pass, so later *additions* to a +# group name cannot affect earlier *uses* of it + +# The following examples should illustrate all this: # you can have a group of people... @staff = sitaram some_dev another-dev @@ -43,10 +47,14 @@ @staff = au.thor # so now "@staff" expands to all 4 names - # groups can include other groups (but not recursively) + # groups can include other groups, and the included group will + # be expanded to whatever value it currently has @interns = indy james @staff = bob @interns # "@staff" expands to 7 names now +@interns = han + # "@interns" now has 3 names in it, but note that this does + # not change @staff # REPO AND BRANCH PERMISSIONS # --------------------------- From 604669ca02cd11b5ebe9375f7a4fbdd898ad2f8c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Sep 2009 18:03:53 +0530 Subject: [PATCH 159/850] rebel edition -- cos when you need it, you need it bad :-) Summary: much as I did not want to use "excludes", I guess if we don't put the code in "master" it's OK to at least *write* (and test) the code! See the example config file for how to use it. See "design choices" section in the "faq, tips, etc" document for how it works. --- README.mkd | 21 ++++++++++++++++++ conf/example.conf | 20 +++++++++++++++++ doc/3-faq-tips-etc.mkd | 50 ++++++++++++++++++++++++++++++++++++++++++ src/gl-compile-conf | 2 +- src/hooks/update | 1 + 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/README.mkd b/README.mkd index 904d9f3..087bf77 100644 --- a/README.mkd +++ b/README.mkd @@ -3,6 +3,27 @@ > [Update 2009-10-28: apart from all the nifty new features, there's now an > "easy install" script in the src directory. This script can be used to > install as well as upgrade a gitolite install. Please see the INSTALL + +> ---- + +> ***REBEL FORK WARNING*** + +> The paranoid half of Sitaram's brain would like to warn you that the other +> half rebelled and created this fork. + +> 1. please read the "design choices" section in the [faqs, tips, etc] +> [fted] document for background +> 2. please read the comments in the example.conf file carefully before +> creating your own config file +> 3. please test your configuration carefully +> 4. if you still have problems and contact me, please mention right up +> front that you're using the ***rebel edition***; makes it easier to +> troubleshoot + +[fted]: http://github.com/sitaramc/gitolite/blob/rebel/doc/3-faq-tips-etc.mkd + +---- + > document in the doc directory for details] ---- diff --git a/conf/example.conf b/conf/example.conf index 6bdc834..9a196bc 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -123,6 +123,26 @@ repo git RW tmp/ = @all RW refs/tags/v[0-9] = junio +# REBEL BRANCH -- DENY/EXCLUDE RULES + +# please see the "faq, tips, etc" document for details on the "rebel" edition. + +# in the example above, you cannot easily say "anyone can write any tag, +# except version tags can only be written by junio". The following might look +# like it works but it doesn't: + + # RW refs/tags/v[0-9] = junio + # RW refs/tags/ = junio linus pasky @others + +# in the "rebel" branch, however, you can do this: + + RW refs/tags/v[0-9] = junio + - refs/tags/v[0-9] = linus pasky @others + RW refs/tags/ = junio linus pasky @others + +# Briefly, the rule is: the first matching refex that has the operation you're +# looking for (`W` or `+`), or a minus (`-`), results in success, or failure, +# respectively. A fallthrough also results in failure # GITWEB AND DAEMON STUFF # ----------------------- diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 7450994..b98050c 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -22,6 +22,7 @@ In this document: * design choices * keeping the parser and the access control separate * why we don't do "excludes" + * how the rebel edition does it anyway :) ### common errors and mistakes @@ -527,3 +528,52 @@ The lack of overlap between refexes ensures ***no confusion*** in specifying, understanding, and ***auditing***, what is allowed and what is not. And in security, "no confusion" is a good thing :-) + +#### how the rebel edition does it anyway :) + +Well, reuss on #git asked, I pointed him to the discussion above and said +"no", and he was nice enough to agree. But then I started thinking that maybe +not everyone needs such "safe" environments. And so, as the README says: + +> The paranoid half of Sitaram's brain would like to warn you that the other +> half rebelled and created this fork. + +Let's recap the **existing semantics**: + +> the first matching refex that has the permission you're looking for (`W` +> or `+`), results in success. A fallthrough results in failure + +First I changed the format of the compiled config file (an internal file that +the user doesn't have to worry about, as long as he remembers to run the +compile script on every upgrade). The format change kept the existing syntax +and semantics of the config file, but it laid the ground work for allowing +"excludes" using **just one extra line of code** (and a very slight change to +another)! + +This extremely small delta between the rebel edition and the main one will +make it trivial to maintain it as a separate branch, regardless of what other +changes happen in the mainline, because I honestly don't see rebel merging +into the mainline. + +Yet ;-) + +Here are the **rebel semantics**, with changes from the "main" one in bold: + +> the first matching refex that has the permission you're looking for (`W` +> or `+`) **or a minus (`-`)**, results in success **or failure, +> respectively**. A fallthrough **also** results in failure + +So the example we started with becomes, in the rebel edition: + + RW refs/tags/v[0-9].* = bruce + - refs/tags/v[0-9].* = @staff + RW refs/tags = @staff + +And here's how it works: + + * for non-version tags, only the 3rd rule matches, so anyone on staff can + push them + * for version tags by bruce, the first rule matches so he can push them + * for version tags by staffers *other than bruce*, the second rule matches + before the third one, and it has a `-` as the permission, so the push + fails diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 583a3f0..e307e2b 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -186,7 +186,7 @@ sub parse_conf_file @repos = expand_list ( @repos ); } # actual permission line - elsif (/^(R|RW|RW\+) (.* )?= (.+)/) + elsif (/^(-|R|RW|RW\+) (.* )?= (.+)/) { my $perms = $1; my @refs; @refs = split(' ', $2) if $2; diff --git a/src/hooks/update b/src/hooks/update index a11b9ad..19f02e8 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -85,6 +85,7 @@ sub check_ref { $refex = (keys %$ar)[0]; # refex? sure -- a regex to match a ref against :) next unless $ref =~ /$refex/; + die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; # as far as *this* ref is concerned we're ok return if ($ar->{$refex} =~ /\Q$perm/); From 601eaf8ea16efc3726d949d17b277239575fe97e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Oct 2009 10:15:55 +0530 Subject: [PATCH 160/850] tips doc: add pointer to later section on excludes --- doc/3-faq-tips-etc.mkd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index b98050c..8763b6e 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -179,6 +179,9 @@ Each refex that allows `W` access (or `+` if this is a rewind) for *this* user, on *this* repo, is matched against the actual refname being updated. If any of the refexes match, the push succeeds. If none of them match, it fails. +The normal gitolite does not allow "exclude" or "deny" rules. But see the +"how the rebel edition does it anyway" below. + #### error checking the config file gitosis does not do any. I just found out that if you mis-spell `members` as From e922dfb939b051a03c34047f2f92bde1245932f6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 16 Nov 2009 20:05:08 +0530 Subject: [PATCH 161/850] compile: allow PATH/foo and populate the hash correctly --- src/gl-compile-conf | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index e307e2b..3f1e231 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -194,9 +194,9 @@ sub parse_conf_file # if no ref is given, this PERM applies to all refs @refs = qw(refs/.*) unless @refs; - # fully qualify refs that dont start with "refs/"; prefix them with - # "refs/heads/" - @refs = map { m(^refs/) or s(^)(refs/heads/); $_ } @refs; + # fully qualify refs that dont start with "refs/" or "PATH/"; + # prefix them with "refs/heads/" + @refs = map { m(^(refs|PATH)/) or s(^)(refs/heads/); $_ } @refs; # expand the user list, unless it is just "@all" @users = expand_list ( @users ) @@ -233,6 +233,13 @@ sub parse_conf_file # for 2nd level check, store each "ref, perms" pair in order for my $ref (@refs) { + # checking PATH based restrictions is expensive for + # the update hook (see the changes to src/hooks/update + # in this commit for why) so we would *very* much like + # to avoid doing it for the large majority of repos + # that do *not* use PATH limits. Setting a flag that + # can be checked right away will help us do that + $repos{$repo}{PATH_LIMITS} = 1 if $ref =~ /^PATH\//; push @{ $repos{$repo}{$user} }, { $ref => $perms } unless $rurp_seen{$repo}{$user}{$ref}{$perms}++; } From 498e62c2f3a90513b90dcc7b916b28387cfdcb0f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 16 Nov 2009 21:45:55 +0530 Subject: [PATCH 162/850] update hook: allow multiple "refs" to be checked --- src/hooks/update | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/hooks/update b/src/hooks/update index 19f02e8..235fa40 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -47,9 +47,6 @@ chomp($merge_base = `git merge-base $oldsha $newsha`) unless $oldsha eq '0' x 40 or $newsha eq '0' x 40; -# some of this is from an example hook in Documentation/howto of git.git, with -# some variations - # what are you trying to do? (is it 'W' or '+'?) my $perm = 'W'; # rewriting a tag is considered a rewind, in terms of permissions @@ -65,6 +62,25 @@ push @allowed_refs, { "$PERSONAL/$ENV{GL_USER}/" => "RW+" } if $PERSONAL; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$ENV{GL_USER}} || [] }; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; +# prepare the list of refs to be checked + +# previously, we just checked $ref -- the ref being updated, which is passed +# to us by git (see man githooks). Now we also have to treat each PATH being +# updated as a potential "ref" and check that, if PATH-based restrictions have +# been specified + +my @refs = ($ref); # the first ref to check is the real one +if (exists $repos{$ENV{GL_REPO}}{PATH_LIMITS}) { + # this is special to git -- the hash of an empty tree + my $empty='4b825dc642cb6eb9a060e54bf8d69288fbee4904'; + # well they're not really "trees" but $empty is indeed the empty tree so + # we can just pretend $oldsha/$newsha are also trees, and anyway 'git + # diff' only wants trees + my $oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha; + my $newtree = $newsha eq '0' x 40 ? $empty : $newsha; + push @refs, map { chomp; s/^/PATH\//; $_; } `git diff --name-only $oldtree $newtree`; +} + my $refex = ''; # check one ref @@ -88,21 +104,25 @@ sub check_ref { die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; # as far as *this* ref is concerned we're ok - return if ($ar->{$refex} =~ /\Q$perm/); + return $refex if ($ar->{$refex} =~ /\Q$perm/); } die "$perm $ref $ENV{GL_REPO} $ENV{GL_USER} DENIED by fallthru\n"; } -# and in this version, we have only one ref to check -check_ref($ref); +# and in this version, we have many "refs" to check. The one we print in the +# log is the *first* one (which is a *real* ref, like refs/heads/master), +# while all the rest (if they exist) are like PATH/something. So we do the +# first one separately to capture it, then run the rest (if any) +my $log_refex = check_ref(shift @refs); +check_ref($_) for @refs; -# if we returned at all, the check succeeded, so we log the action and exit 0 +# if we returned at all, all the checks succeeded, so we log the action and exit 0 # logging note: if log failure isn't important enough to block pushes, get rid # of all the error checking open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n"; print $log_fh "$ENV{GL_TS} $perm\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . - "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\t$refex\n"; + "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\t$log_refex\n"; close $log_fh or die "close log failed: $!\n"; exit 0; From d71720d050089ae2443ce9b4b64d521f43ee67ef Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Dec 2009 07:15:05 +0530 Subject: [PATCH 163/850] fold rebel into master :) [please read] Well, something even more outrageous than deny rules and path-based limits came along, so I decided that "rebel" was actually quite "conformist" in comparision ;-) Jokes apart, the fact is that the access control rules, even when using deny rules and path-limits, are still *auditable*. Which means it is good enough for "corporate use". [The stuff that I'm working on now takes away the auditability aspect -- individual users can "own" repos, create rules for themselves, etc. So let's just say that is the basis of distinguishing "master" now.] --- README.mkd | 26 +------ conf/example.conf | 13 +++- doc/3-faq-tips-etc.mkd | 156 ++++++++++++++--------------------------- 3 files changed, 63 insertions(+), 132 deletions(-) diff --git a/README.mkd b/README.mkd index 087bf77..b766f7e 100644 --- a/README.mkd +++ b/README.mkd @@ -3,28 +3,7 @@ > [Update 2009-10-28: apart from all the nifty new features, there's now an > "easy install" script in the src directory. This script can be used to > install as well as upgrade a gitolite install. Please see the INSTALL - -> ---- - -> ***REBEL FORK WARNING*** - -> The paranoid half of Sitaram's brain would like to warn you that the other -> half rebelled and created this fork. - -> 1. please read the "design choices" section in the [faqs, tips, etc] -> [fted] document for background -> 2. please read the comments in the example.conf file carefully before -> creating your own config file -> 3. please test your configuration carefully -> 4. if you still have problems and contact me, please mention right up -> front that you're using the ***rebel edition***; makes it easier to -> troubleshoot - -[fted]: http://github.com/sitaramc/gitolite/blob/rebel/doc/3-faq-tips-etc.mkd - ----- - -> document in the doc directory for details] +> document for details] ---- @@ -100,8 +79,7 @@ detail [here][gsdiff]. a synonym for "annotate" :-)] * "personal namespace" prefix for each dev * migration guide and simple converter for gitosis conf file - * "exclude" (or "deny") rights in the config file -- this is the "rebel" - branch in the repository, and always will be ;-) + * "exclude" (or "deny") rights at the branch/tag level ### security diff --git a/conf/example.conf b/conf/example.conf index 9a196bc..64ac059 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -99,6 +99,10 @@ repo @oss_repos # ADVANCED PERMISSIONS USING REFEXES # - refexes are specified in perl regex syntax +# - refexes are matched without any anchoring, which means a refex like +# "refs/tags/v[0-9]" matches anything *containing* that pattern. There +# may be text before and after it (example: refs/tags/v4-r3p7), and it +# will still match # - if no refex appears, the rule applies to all refs in that repo # - a refex is automatically prefixed by "refs/heads/" if it doesn't start # with "refs/" (so tags have to be explicitly named as @@ -123,9 +127,11 @@ repo git RW tmp/ = @all RW refs/tags/v[0-9] = junio -# REBEL BRANCH -- DENY/EXCLUDE RULES +# DENY/EXCLUDE RULES -# please see the "faq, tips, etc" document for details on the "rebel" edition. +# ***IMPORTANT NOTE: if you use deny rules, the order of the rules also makes +# a difference, where earlier it did not. Please review your ruleset +# carefully or test it***. Or ask me. # in the example above, you cannot easily say "anyone can write any tag, # except version tags can only be written by junio". The following might look @@ -134,7 +140,8 @@ repo git # RW refs/tags/v[0-9] = junio # RW refs/tags/ = junio linus pasky @others -# in the "rebel" branch, however, you can do this: +# if you use "deny" rules, however, you can do this (a "deny" rule just uses +# "-" instead of "R" or "RW" or "RW+" in the permission field) RW refs/tags/v[0-9] = junio - refs/tags/v[0-9] = linus pasky @others diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 8763b6e..c112fd4 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -17,12 +17,10 @@ In this document: * one user, many keys * support for git installed outside default PATH * what repos do I have access to? - * other cool things + * "exclude" (or "deny") rules * "personal" branches * design choices * keeping the parser and the access control separate - * why we don't do "excludes" - * how the rebel edition does it anyway :) ### common errors and mistakes @@ -179,8 +177,8 @@ Each refex that allows `W` access (or `+` if this is a rewind) for *this* user, on *this* repo, is matched against the actual refname being updated. If any of the refexes match, the push succeeds. If none of them match, it fails. -The normal gitolite does not allow "exclude" or "deny" rules. But see the -"how the rebel edition does it anyway" below. +Gitolite also allows "exclude" or "deny" rules. See later in this document +for details. #### error checking the config file @@ -404,7 +402,54 @@ Note that until this version, we used to put out an ugly `need SSH_ORIGINAL_COMMAND` error, just like gitosis used to. All we did is put that code path to better use :-) -### other cool things +#### "exclude" (or "deny") rules + +Take a look at the following snippet, which *seems* to say that "bruce" can +write versioned tags (anything containing `refs/tags/v[0-9]`), but the other +staffers can't: + + @staff = bruce whitfield martin + [... and later ...] + RW refs/tags/v[0-9] = bruce + RW refs/tags = @staff + +But that's not how the matching works. As long as any refex matches the +refname being updated, it's a "yes". Since the second refex (which says +"anything containing `refs/tags`") is a superset of the first one, it lets +anyone on `@staff` create versioned tags, not just Bruce. + +One way to fix this is to allow "excludes" -- some changes in syntax, combined +with a rigorous, ordered, interpretation would do it. + +Let's recap the **existing semantics**: + +> the first matching refex that has the permission you're looking for (`W` +> or `+`), results in success. A fallthrough results in failure + +Here are the **new semantics**, with changes from the "main" one in bold: + +> the first matching refex that has the permission you're looking for (`W` +> or `+`) **or a minus (`-`)**, results in success **or failure, +> respectively**. A fallthrough **also** results in failure + +So the example we started with becomes, if you use "deny" rules: + + RW refs/tags/v[0-9] = bruce + - refs/tags/v[0-9] = @staff + RW refs/tags = @staff + +And here's how it works: + + * for non-version tags, only the 3rd rule matches, so anyone on staff can + push them + * for version tags by bruce, the first rule matches so he can push them + * for version tags by staffers *other than bruce*, the second rule matches + before the third one, and it has a `-` as the permission, so the push + fails + +***IMPORTANT NOTE: if you use deny rules, the order of the rules also makes a +difference, where earlier it did not. Please review your ruleset carefully or +test it***. Or ask me. #### "personal" branches @@ -481,102 +526,3 @@ have to be first "compiled", and the access control programs use this If you choose the "easy install" method, all this is quite transparent to you anyway. If you cannot use the easy install and must install manually, I have clear instructions on how to set it up. - -#### why we don't do "excludes" - -[umm... having said all this, I implemented it anyway; see the "rebel" -branch!] - -I found an error in the example conf file. This snippet *seems* to say that -"bruce" can write versioned tags (`refs/tags/v[0-9].*`), but the other -staffers can't: - - @staff = bruce whitfield martin - [... and later ...] - RW refs/tags/v[0-9].* = bruce - RW refs/tags = @staff - -But that's not how the matching works. As long as any refex matches the -refname being updated, it's a "yes". So the second refex lets anyone on -`@staff` create versioned tags, not just Bruce. - -One way to fix this is to allow "excludes" -- some changes in syntax, combined -with a rigorous, ordered, interpretation would do it. - -But if you're ever played with squid ACLs, or the include/exclude rules for -rsync, or rdiff-backup, or even git's own ignore mechanism, you'll see why I -won't do this. It bloats the code and the docs, and, despite all the docs, -*still* confuses people, which may then *reduce* security! - -Squid, rsync, gitignore, and all *need* the feature and so tolerate all this; -but we don't need it. All we need to do is make the refexes *disjoint* in -what they match (i.e., ensure that no refname can be matched by more than one -refex): - - RW refs/tags/v[0-9].* = bruce - RW refs/tags/staff/ = @staff - -In general, you probably want to control the refnames writable by devs anyway, -if at least to maintain some sanity, so being forced to make the refexes -disjoint is not a big problem. Here's an example: only the `project_lead` can -make arbitrarily named refs, while the rest have to stay within their assigned -namespaces: - - RW+ = project_lead - RW refs/tags/qa/ = @qa_team - RW bugID/ = @dev_team - RW trac/ = @dev_team - -The lack of overlap between refexes ensures ***no confusion*** in specifying, -understanding, and ***auditing***, what is allowed and what is not. - -And in security, "no confusion" is a good thing :-) - -#### how the rebel edition does it anyway :) - -Well, reuss on #git asked, I pointed him to the discussion above and said -"no", and he was nice enough to agree. But then I started thinking that maybe -not everyone needs such "safe" environments. And so, as the README says: - -> The paranoid half of Sitaram's brain would like to warn you that the other -> half rebelled and created this fork. - -Let's recap the **existing semantics**: - -> the first matching refex that has the permission you're looking for (`W` -> or `+`), results in success. A fallthrough results in failure - -First I changed the format of the compiled config file (an internal file that -the user doesn't have to worry about, as long as he remembers to run the -compile script on every upgrade). The format change kept the existing syntax -and semantics of the config file, but it laid the ground work for allowing -"excludes" using **just one extra line of code** (and a very slight change to -another)! - -This extremely small delta between the rebel edition and the main one will -make it trivial to maintain it as a separate branch, regardless of what other -changes happen in the mainline, because I honestly don't see rebel merging -into the mainline. - -Yet ;-) - -Here are the **rebel semantics**, with changes from the "main" one in bold: - -> the first matching refex that has the permission you're looking for (`W` -> or `+`) **or a minus (`-`)**, results in success **or failure, -> respectively**. A fallthrough **also** results in failure - -So the example we started with becomes, in the rebel edition: - - RW refs/tags/v[0-9].* = bruce - - refs/tags/v[0-9].* = @staff - RW refs/tags = @staff - -And here's how it works: - - * for non-version tags, only the 3rd rule matches, so anyone on staff can - push them - * for version tags by bruce, the first rule matches so he can push them - * for version tags by staffers *other than bruce*, the second rule matches - before the third one, and it has a `-` as the permission, so the push - fails From e7e608535116a8f4257c467a2e54e26007cbb839 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Dec 2009 21:54:23 +0530 Subject: [PATCH 164/850] compile: fix description and export-ok problem part of comment on b78a720ceeecf7db985dd8d013c08829ea8cfc81: The only reason it's getting into master is because it looks cool! I hate it when something that looks cool doesn't work right :( creating a repo on gitolite-admin push is *needed* in order to get descriptions and export-ok files to work right --- src/gl-compile-conf | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 3f1e231..f79b713 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -320,6 +320,20 @@ my ($gv_maj, $gv_min, $gv_patchrel) = ($git_version =~ m/git version (\d+)\.(\d+ die "$ABRT I can't understand $git_version\n" unless ($gv_maj >= 1); $git_version = $gv_maj*10000 + $gv_min*100 + $gv_patchrel; # now it's "normalised" +# repo-base needs to be an absolute path for this loop to work right +# so if it was not already absolute, prefix $HOME. +my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); + +wrap_chdir("$repo_base_abs"); + +for my $repo (sort keys %repos) { + unless (-d "$repo.git") { + new_repo($repo, "$GL_ADMINDIR/src/hooks"); + # new_repo would have chdir'd us away; come back + wrap_chdir("$repo_base_abs"); + } +} + warn "\n\t\t***** WARNING *****\n" . "\tyour git version is older than 1.6.2\n" . "\tgitolite will work but you MUST read the section on\n" . @@ -335,10 +349,6 @@ warn "\n\t\t***** WARNING *****\n" . # :-) These are now "pseduo users" -- giving them "R" access to a repo is all # you have to do -# repo-base needs to be an absolute path for this loop to work right -# so if it was not already absolute, prefix $HOME. -my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); - wrap_chdir("$repo_base_abs"); # daemons first... From a283b8ad4948ebaed9cfe44ad4210df28deeeebf Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Dec 2009 22:07:46 +0530 Subject: [PATCH 165/850] compile: kill preceding space when killing comments consider: repo = "some desc" # some comment (and note that the regex for recognising a description expects that dblquote to be the *last* character on the line) --- src/gl-compile-conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index f79b713..bbb2eb6 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -165,7 +165,7 @@ sub parse_conf_file s/^ //; s/ $//; # kill comments - s/#.*//; + s/\s*#.*//; # and blank lines next unless /\S/; From c3dbdae134b36498b2b6540a144b46d50209fbe3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 2 Dec 2009 11:49:58 +0530 Subject: [PATCH 166/850] easy install tail message was apparently too confusing --- src/gl-easy-install | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index cf3a859..db20e2f 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -581,15 +581,14 @@ CONFIG FILE FORMAT: see comments in conf/example.conf in the gitolite source. SSH MAGIC: Remember you (the admin) now have *two* keys to access the server hosting your gitolite setup -- one to get you a command line, and one to get you gitolite access; see doc/6-ssh-troubleshooting.mkd. If you're not using -keychain or some such software, you may have to run this each time you log in: +keychain or some such software, you may have to run an 'ssh-add' command to +add that key each time you log in. - ssh-add ~/.ssh/\$admin_name +URLS: *Your* URL for cloning any repo on this server is different from the +url that the *other* users have to use. The easy install command should tell +you what these URLs look like, at the end of each successful run. Feel free +to re-run easy install again (using the same arguments) if you missed it. -URLS: *Your* URL for cloning any repo on this server will be - - gitolite:reponame - -*Other* users you set up will have to use - - \$user@\$host:reponame +UPGRADING GITOLITE: just pull a fresh clone from github, and run the same easy +install command as before, with the same arguments. " From e6da853082689ff6c44c57e29574a2c85c90c9c1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 4 Dec 2009 09:51:22 +0530 Subject: [PATCH 167/850] auth, compile, pm: good bit of refactoring all of this is prep for the upcoming, all-new, chrome-plated, "wildrepos" branch :) - many variables go to gitolite.pm now, and are "our"d into the other files as needed - new functions parse_acl, report_basic to replace inlined code --- src/gitolite.pm | 75 ++++++++++++++++++++++++++++++++++++++------- src/gl-auth-command | 37 ++++++++-------------- src/gl-compile-conf | 11 +++---- 3 files changed, 82 insertions(+), 41 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index c7276bd..71c0b0c 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -13,6 +13,35 @@ # - define a function that tells you where to find the rc file # - define a function that creates a new repo and give it our update hook +# ---------------------------------------------------------------------------- +# common definitions +# ---------------------------------------------------------------------------- + +$ABRT = "\n\t\t***** ABORTING *****\n "; +$WARN = "\n\t\t***** WARNING *****\n "; + +# commands we're expecting +$R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/; +$W_COMMANDS=qr/^git[ -]receive-pack$/; + +# note that REPONAME_PATT allows a "/" also, which USERNAME_PATT doesn't +$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern +$USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern + +# ---------------------------------------------------------------------------- +# convenience subs +# ---------------------------------------------------------------------------- + +sub wrap_chdir { + chdir($_[0]) or die "$ABRT chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; +} + +sub wrap_open { + open (my $fh, $_[0], $_[1]) or die "$ABRT open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n" . + ( $_[2] || '' ); # suffix custom error message if given + return $fh; +} + # ---------------------------------------------------------------------------- # where is the rc file hiding? # ---------------------------------------------------------------------------- @@ -43,17 +72,9 @@ sub where_is_rc } } -$ABRT = "\n\t\t***** ABORTING *****\n "; -$WARN = "\n\t\t***** WARNING *****\n "; -sub wrap_chdir { - chdir($_[0]) or die "$ABRT chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; -} - -sub wrap_open { - open (my $fh, $_[0], $_[1]) or die "$ABRT open $_[1] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n" . - ( $_[2] || '' ); # suffix custom error message if given - return $fh; -} +# ---------------------------------------------------------------------------- +# create a new repository +# ---------------------------------------------------------------------------- # NOTE: this sub will change your cwd; caller beware! sub new_repo @@ -71,4 +92,36 @@ sub new_repo chmod 0755, "hooks/update"; } +# ---------------------------------------------------------------------------- +# parse the compiled acl +# ---------------------------------------------------------------------------- + +sub parse_acl +{ + my $GL_CONF_COMPILED = shift; + die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; +} + +# ---------------------------------------------------------------------------- +# print a report of $user's basic permissions +# ---------------------------------------------------------------------------- + +# basic means wildcards will be shown as wildcards; this is pretty much what +# got parsed by the compile script +sub report_basic +{ + my($GL_ADMINDIR, $GL_CONF_COMPILED, $user) = @_; + + &parse_acl($GL_CONF_COMPILED); + + # send back some useful info if no command was given + print "hello $user, the gitolite version here is "; + system("cat", "$GL_ADMINDIR/src/VERSION"); + print "\ryou have the following permissions:\n\r"; + for my $r (sort keys %repos) { + my $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{$r}{R}{$user} ? ' R' : '' ) ); + $perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{$r}{W}{$user} ? ' W' : '' ) ); + print "$perm\t$r\n\r" if $perm; + } +} 1; diff --git a/src/gl-auth-command b/src/gl-auth-command index a135be6..86fec88 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -24,7 +24,10 @@ use warnings; # ---------------------------------------------------------------------------- +# these are set by the "rc" file our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $GL_ADMINDIR); +# and these are set by gitolite.pm +our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT); our %repos; # the common setup module is in the same directory as this running program is @@ -35,20 +38,10 @@ require "$bindir/gitolite.pm"; # ask where the rc file is, get it, and "do" it &where_is_rc(); die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; -# then "do" the compiled config file, whose name we now know -die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; # add a custom path for git binaries, if specified $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; -# ---------------------------------------------------------------------------- -# definitions specific to this program -# ---------------------------------------------------------------------------- - -my $R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/; -my $W_COMMANDS=qr/^git[ -]receive-pack$/; -my $REPONAME_PATT=qr(^[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern - # ---------------------------------------------------------------------------- # start... # ---------------------------------------------------------------------------- @@ -62,15 +55,7 @@ my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! # SSH_ORIGINAL_COMMAND must exist; if not, we die with a nice message unless ($ENV{SSH_ORIGINAL_COMMAND}) { - # send back some useful info if no command was given - print "hello $user, the gitolite version here is "; - system("cat", "$GL_ADMINDIR/src/VERSION"); - print "\ryou have the following permissions:\n\r"; - for my $r (sort keys %repos) { - my $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{$r}{R}{$user} ? ' R' : '' ) ); - $perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{$r}{W}{$user} ? ' W' : '' ) ); - print "$perm\t$r\n\r" if $perm; - } + &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); exit 1; } @@ -93,6 +78,9 @@ die "bad command: $cmd. Make sure the repo name is exactly as in your config\n" # first level permissions check # ---------------------------------------------------------------------------- +# parse the compiled acl; goes into %repos (global) +&parse_acl($GL_CONF_COMPILED); + # we know the user and repo; we just need to know what perm he's trying my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W'); @@ -102,11 +90,12 @@ die "$perm access for $repo DENIED to $user\n" # create the repo if it doesn't already exist and the user has "W" access my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); -if ( ( $repos{$repo}{W}{$user} - or $repos{$repo}{W}{'@all'} ) and not -d "$repo_base_abs/$repo.git" ) { - wrap_chdir("$repo_base_abs"); - new_repo($repo, "$GL_ADMINDIR/src/hooks"); - wrap_chdir($ENV{HOME}); +if ( not -d "$repo_base_abs/$repo.git" ) { + if ( $repos{$repo}{W}{$user} or $repos{$repo}{W}{'@all'} ) { + wrap_chdir("$repo_base_abs"); + new_repo($repo, "$GL_ADMINDIR/src/hooks"); + wrap_chdir($ENV{HOME}); + } } # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index bbb2eb6..56b11ed 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -51,8 +51,10 @@ $Data::Dumper::Sortkeys = 1; # setup quiet mode if asked; please do not use this when running manually open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); +# these are set by the "rc" file our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH); -our ($ABRT, $WARN); +# and these are set by gitolite.pm +our ($REPONAME_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); # the common setup module is in the same directory as this running program is my $bindir = $0; @@ -71,11 +73,8 @@ $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; # ---------------------------------------------------------------------------- # command and options for authorized_keys -my $AUTH_COMMAND="$GL_ADMINDIR/src/gl-auth-command"; -my $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; -# note that REPONAME_PATT allows a "/" also, which USERNAME_PATT doesn't -my $REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern -my $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern +$AUTH_COMMAND="$GL_ADMINDIR/src/gl-auth-command"; +$AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; # groups can now represent user groups or repo groups. From 8a4bb453a0d40e90cfab79997c6a32aa8e468e6b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 5 Dec 2009 14:41:08 +0530 Subject: [PATCH 168/850] document that @all doesnt work as expected in deny rules @all in a deny rule doesnt work as it might look in the config file, because @all rights are checked last. This is fine if you dont have any DENYs (and so rule order doesn't matter), but with DENY it causes some problems. I never bothered to document it because I did not expect that any repo that is "serious" enough to have deny rules *at all* should then allow *any* kind of "write* access to @all. That's a very big contradiction in terms of paranoia! Translation: this will not be supported. Don't bother asking. You know who you are :) --- conf/example.conf | 3 ++- doc/3-faq-tips-etc.mkd | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 64ac059..b5ead17 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -131,7 +131,8 @@ repo git # ***IMPORTANT NOTE: if you use deny rules, the order of the rules also makes # a difference, where earlier it did not. Please review your ruleset -# carefully or test it***. Or ask me. +# carefully or test it. In particular, do not use `@all` in a deny rule -- it +# won't work as you might expect***. # in the example above, you cannot easily say "anyone can write any tag, # except version tags can only be written by junio". The following might look diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index c112fd4..464a4e5 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -404,6 +404,11 @@ that code path to better use :-) #### "exclude" (or "deny") rules +***IMPORTANT CAVEAT: if you use deny rules, the order of the rules also makes +a difference, where earlier it did not. Please review your ruleset carefully +or test it. In particular, do not use `@all` in a deny rule -- it won't work +as you might expect***. + Take a look at the following snippet, which *seems* to say that "bruce" can write versioned tags (anything containing `refs/tags/v[0-9]`), but the other staffers can't: @@ -447,10 +452,6 @@ And here's how it works: before the third one, and it has a `-` as the permission, so the push fails -***IMPORTANT NOTE: if you use deny rules, the order of the rules also makes a -difference, where earlier it did not. Please review your ruleset carefully or -test it***. Or ask me. - #### "personal" branches "personal" branches are great for corporate environments, where From 77306567e96b2551c1bf126d80eceb0b176093ee Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 5 Dec 2009 18:38:46 +0530 Subject: [PATCH 169/850] wildrepos: teach compile the new syntax There's a new "C" permission to let someone *create* a repo that matches the pattern given in the "repo ..." line. If the word CREATER appears in the repo pattern, then that is forced to the actual user performing that operation. Something like this (we'll discuss READERS and WRITERS later): repo personal/CREATER/.+ C = @staff R [foo] = READERS RW [bar] = WRITERS ...various other permissions as usual... Delegation checking also changes quite a bit... see comments in code Implementation: there's also a sneaky little trick we're playing here with the dumped hash --- src/gitolite.pm | 2 ++ src/gl-compile-conf | 66 ++++++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 71c0b0c..5864466 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -27,6 +27,8 @@ $W_COMMANDS=qr/^git[ -]receive-pack$/; # note that REPONAME_PATT allows a "/" also, which USERNAME_PATT doesn't $REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern +# same as REPONAME, plus some common regex metas +$REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._/-]*$); # ---------------------------------------------------------------------------- # convenience subs diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 56b11ed..54448f6 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -54,7 +54,7 @@ open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH); # and these are set by gitolite.pm -our ($REPONAME_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); +our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); # the common setup module is in the same directory as this running program is my $bindir = $0; @@ -85,7 +85,8 @@ our %groups = (); # %repos has two functions. # $repos{repo}{R|W}{user} = 1 if user has R (or W) permissions for at least -# one branch in repo. This is used by the "level 1 check" (see faq) +# one branch in repo. This is used by the "level 1 check" (see faq). There's +# also the new "C" (create a repo) permission now # $repos{repo}{user} is a list of {ref, perms} pairs. This is used by the # level 2 check. In order to allow "exclude" rules, the order of rules now @@ -119,9 +120,7 @@ sub expand_list for my $item (@list) { - # we test with the slightly more relaxed pattern here; we'll catch the - # "/" in user name thing later; it doesn't affect security anyway - die "$ABRT bad user or repo name $item\n" unless $item =~ $REPONAME_PATT; + die "$ABRT bad user or repo name $item\n" unless $item =~ $REPOPATT_PATT; if ($item =~ /^@/) # nested group { die "$ABRT undefined group $item\n" unless $groups{$item}; @@ -183,9 +182,11 @@ sub parse_conf_file # grab the list and expand any @stuff in it @repos = split ' ', $1; @repos = expand_list ( @repos ); + + s/\bCREAT[EO]R\b/\$creater/g for @repos; } # actual permission line - elsif (/^(-|R|RW|RW\+) (.* )?= (.+)/) + elsif (/^(-|C|R|RW|RW\+) (.* )?= (.+)/) { my $perms = $1; my @refs; @refs = split(' ', $2) if $2; @@ -202,21 +203,39 @@ sub parse_conf_file unless (@users == 1 and $users[0] eq '@all'); do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; + s/\bCREAT[EO]R\b/\$creater/g for @users; + s/\bREADERS\b/\$readers/g for @users; + s/\bWRITERS\b/\$writers/g for @users; + # ok, we can finally populate the %repos hash for my $repo (@repos) # each repo in the current stanza { # if we're processing a delegated config file (not the master - # config), and if that fragment name is not the same as the - # current repo - if ($fragment ne 'master' and $fragment ne $repo) - { - # then the fragment must be a group name and the repo - # being processed must be a member of that "@group". - # Also, the value of the hash for that combination must be - # "master", signifying a group created in the master - # config file and not in one of the delegates - unless ( ($groups{"\@$fragment"}{$repo} || '') eq 'master') - { + # config), we need to prevent attempts by that admin to obtain + # rights on stuff outside his domain + + # trying to set access for $repo (='foo')... + if ( + # processing the master config, not a fragment + ( $fragment eq 'master' ) or + # fragment is also called 'foo' (you're allowed to have a + # fragment that is only concerned with one repo) + ( $fragment eq $repo ) or + # fragment is called "bar" and "@bar = foo" has been + # defined in the master config + ( ($groups{"\@$fragment"}{$repo} || '') eq 'master' ) + ) { + # all these are fine + } else { + # this is a little more complex + + # fragment is called "bar", one or more "@bar = regex" + # have been specified in master, and "foo" matches some + # such "regex" + my @matched = grep { $repo =~ /^$_$/ } + grep { $groups{"\@$fragment"}{$_} eq 'master' } + sort keys %{ $groups{"\@$fragment"} }; + if (@matched < 1) { $ignored{$fragment}{$repo} = 1; next; } @@ -226,6 +245,7 @@ sub parse_conf_file $user_list{$user}++; # only to catch lint, see later # for 1st level check (see faq/tips doc) + $repos{$repo}{C}{$user} = 1, next if $perms eq 'C'; $repos{$repo}{R}{$user} = 1 if $perms =~ /R/; $repos{$repo}{W}{$user} = 1 if $perms =~ /W/; @@ -302,7 +322,12 @@ for my $fragment_file (glob("conf/fragments/*.conf")) } my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); -print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]); +my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); +# the dump uses single quotes, but we convert any strings containing $creater, +# $readers, $writers, to double quoted strings. A wee bit sneaky, but not too +# much... +$dumped_data =~ s/'(?=[^']*\$(?:creater|readers|writers))(.*?)'/"$1"/g; +print $compiled_fh $dumped_data; close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; # ---------------------------------------------------------------------------- @@ -326,6 +351,7 @@ my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" wrap_chdir("$repo_base_abs"); for my $repo (sort keys %repos) { + next unless $repo =~ $REPONAME_PATT; unless (-d "$repo.git") { new_repo($repo, "$GL_ADMINDIR/src/hooks"); # new_repo would have chdir'd us away; come back @@ -352,6 +378,7 @@ wrap_chdir("$repo_base_abs"); # daemons first... for my $repo (sort keys %repos) { + next unless $repo =~ $REPONAME_PATT; my $export_ok = "$repo.git/git-daemon-export-ok"; if ($repos{$repo}{'R'}{'daemon'}) { system("touch $export_ok"); @@ -363,6 +390,7 @@ for my $repo (sort keys %repos) { my %projlist = (); # ...then gitwebs for my $repo (sort keys %repos) { + next unless $repo =~ $REPONAME_PATT; my $desc_file = "$repo.git/description"; # note: having a description also counts as enabling gitweb if ($repos{$repo}{'R'}{'gitweb'} or $desc{"$repo.git"}) { @@ -422,7 +450,7 @@ for my $pubkey (glob("*")) # lint check 3; a little more severe than the first two I guess... for my $user (sort keys %user_list) { - next if $user =~ /^(gitweb|daemon|\@all)$/ or $user_list{$user} eq 'has pubkey'; + next if $user =~ /^(gitweb|daemon|\@all|\$creater|\$readers|\$writers)$/ or $user_list{$user} eq 'has pubkey'; print STDERR "$WARN user $user in config, but has no pubkey!\n"; } From f49eddd6607f9722a4a4d948287bb52a64802ad2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 5 Dec 2009 22:39:56 +0530 Subject: [PATCH 170/850] wildrepos: teach auth and update hook about wildcard repos - new_repo now takes a "creater" parameter; if given, this user is recorded (in a file called "gl-creater") as the creater of the repo. Only applicable to wildcards - repo_rights reads "gl-creater" and "gl-perms" to tell you who created it, and whether you (the $user) are in the list of READERS or WRITERS **NOTE** that the mechanism to create/update gl-perms has not been written yet... (as of this commit) - parse_acl takes 4 more arguments, all optional. The repo name we're interested in (set by all except the access reporting function), and the names to be interpolated as $creater, $readers, writers - report_basic now knows about the "C" permission and shows it - auth now autovivifies a repo if the user has "C" and it's a wildcard match, or (the old case) the user has "W" and it's not a wildcard. In the former case, the creater is also set IMPLEMENTATION NOTES: - the Dumper code now uses a custom hash key sort to make sure $creater etc land up at the *end* - a wee bit of duplication exists in the update hook; it borrows a little code from parse_acl. I dont (yet) want to include all of gitolite.pm for that little piece... --- src/gitolite.pm | 76 +++++++++++++++++++++++++++++++++++++++++---- src/gl-auth-command | 36 +++++++++++++-------- src/gl-compile-conf | 10 ++++++ src/hooks/update | 23 +++++++++++--- 4 files changed, 121 insertions(+), 24 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 5864466..f341ab1 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -81,7 +81,7 @@ sub where_is_rc # NOTE: this sub will change your cwd; caller beware! sub new_repo { - my ($repo, $hooks_dir) = @_; + my ($repo, $hooks_dir, $creater) = @_; umask($REPO_UMASK); @@ -89,19 +89,81 @@ sub new_repo # erm, note that's "and die" not "or die" as is normal in perl wrap_chdir("$repo.git"); system("git --bare init >&2"); + system("echo $creater > gl-creater") if $creater; # propagate our own, plus any local admin-defined, hooks system("cp $hooks_dir/* hooks/"); chmod 0755, "hooks/update"; } +# ---------------------------------------------------------------------------- +# metaphysics (like, "is there a god?", "who created me?", etc) +# ---------------------------------------------------------------------------- + +# "who created this repo", "am I on the R list", and "am I on the RW list"? +sub repo_rights +{ + my ($repo_base_abs, $repo, $user) = @_; + # creater + my $c = ''; + if ( -f "$repo_base_abs/$repo.git/gl-creater") { + my $fh = wrap_open("<", "$repo_base_abs/$repo.git/gl-creater"); + chomp($c = <$fh>); + } + # $user's R and W rights + my ($r, $w); $r = ''; $w = ''; + if ($user and -f "$repo_base_abs/$repo.git/gl-perms") { + my $fh = wrap_open("<", "$repo_base_abs/$repo.git/gl-perms"); + my $perms = join ("", <$fh>); + if ($perms) { + $r = $user if $perms =~ /^\s*R(?=\s).*\s$user(\s|$)/m; + $w = $user if $perms =~ /^\s*RW(?=\s).*\s$user(\s|$)/m; + } + } + + return ($c, $r, $w); +} + # ---------------------------------------------------------------------------- # parse the compiled acl # ---------------------------------------------------------------------------- sub parse_acl { - my $GL_CONF_COMPILED = shift; + # IMPLEMENTATION NOTE: a wee bit of this is duplicated in the update hook; + # please update that also if the interface or the env vars change + + my ($GL_CONF_COMPILED, $repo, $c, $r, $w) = @_; + + # void $r if same as $w (otherwise "readers" overrides "writers"; this is + # the same problem that needed a sort sub for the Dumper in the compile + # script, but localised to just $readers and $writers) + $r = "" if $r eq $w; + + # set up the variables for a parse to interpolate stuff from the dumped + # hash (remember the selective conversion of single to double quotes?). + + # if they're not passed in, then we look for an env var of that name, else + # we default to "NOBODY" (we hope there isn't a real user called NOBODY!) + # And in any case, we set those env vars so level 2 can redo the last + # parse without any special code + + our $creater = $ENV{GL_CREATER} = $c || $ENV{GL_CREATER} || "NOBODY"; + our $readers = $ENV{GL_READERS} = $r || $ENV{GL_READERS} || "NOBODY"; + our $writers = $ENV{GL_WRITERS} = $w || $ENV{GL_WRITERS} || "NOBODY"; + die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; + + # access reporting doesn't send $repo, and doesn't need to + return unless $repo; + + return $ENV{GL_REPOPATT} = "" if $repos{$repo}; + my @matched = grep { $repo =~ /^$_$/ } sort keys %repos; + die "$repo has no matches\n" unless @matched; + die "$repo has multiple matches\n@matched\n" if @matched > 1; + # found exactly one pattern that matched, copy its ACL + $repos{$repo} = $repos{$matched[0]}; + # and return the pattern + return $ENV{GL_REPOPATT} = $matched[0]; } # ---------------------------------------------------------------------------- @@ -114,16 +176,18 @@ sub report_basic { my($GL_ADMINDIR, $GL_CONF_COMPILED, $user) = @_; - &parse_acl($GL_CONF_COMPILED); + &parse_acl($GL_CONF_COMPILED, "", "CREATER", "READERS", "WRITERS"); # send back some useful info if no command was given print "hello $user, the gitolite version here is "; system("cat", "$GL_ADMINDIR/src/VERSION"); print "\ryou have the following permissions:\n\r"; for my $r (sort keys %repos) { - my $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{$r}{R}{$user} ? ' R' : '' ) ); - $perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{$r}{W}{$user} ? ' W' : '' ) ); - print "$perm\t$r\n\r" if $perm; + my $perm .= ( $repos{$r}{C}{'@all'} ? ' @' : ( $repos{$r}{C}{$user} ? ' C' : ' ' ) ); + $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{$r}{R}{$user} ? ' R' : ' ' ) ); + $perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ) ); + print "$perm\t$r\n\r" if $perm =~ /\S/; } } 1; + diff --git a/src/gl-auth-command b/src/gl-auth-command index 86fec88..eff0d77 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -78,8 +78,28 @@ die "bad command: $cmd. Make sure the repo name is exactly as in your config\n" # first level permissions check # ---------------------------------------------------------------------------- -# parse the compiled acl; goes into %repos (global) -&parse_acl($GL_CONF_COMPILED); +my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); + +if ( -d "$repo_base_abs/$repo.git" ) { + # existing repo + my ($creater, $user_R, $user_W) = &repo_rights($repo_base_abs, $repo, $user); + my $patt = &parse_acl($GL_CONF_COMPILED, $repo, $creater, $user_R, $user_W); +} else { + my $patt = &parse_acl($GL_CONF_COMPILED, $repo, $user, $user, $user); + # parse_acl returns "" if the repo was non-wildcard, or the pattern + # that matched if it was a wildcard + + # auto-vivify new repo; 2 situations allow autoviv -- normal repos + # with W access (the old mode), and wildcard repos with C access + my $W_ok = $repos{$repo}{W}{$user} || $repos{$repo}{W}{'@all'}; + my $C_ok = $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'}; + if ($W_ok and not $patt or $C_ok and $patt) { + wrap_chdir("$repo_base_abs"); + # for wildcard repos, we also want to set the "creater" + new_repo($repo, "$GL_ADMINDIR/src/hooks", ( $patt ? $user : "")); + wrap_chdir($ENV{HOME}); + } +} # we know the user and repo; we just need to know what perm he's trying my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W'); @@ -88,16 +108,6 @@ die "$perm access for $repo DENIED to $user\n" unless $repos{$repo}{$perm}{$user} or $repos{$repo}{$perm}{'@all'}; -# create the repo if it doesn't already exist and the user has "W" access -my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); -if ( not -d "$repo_base_abs/$repo.git" ) { - if ( $repos{$repo}{W}{$user} or $repos{$repo}{W}{'@all'} ) { - wrap_chdir("$repo_base_abs"); - new_repo($repo, "$GL_ADMINDIR/src/hooks"); - wrap_chdir($ENV{HOME}); - } -} - # ---------------------------------------------------------------------------- # logging, timestamp. also setup env vars for later # ---------------------------------------------------------------------------- @@ -113,7 +123,7 @@ for ($s, $min, $h, $d, $m) { } $ENV{GL_TS} = "$y-$m-$d.$h:$min:$s"; -# substitute template parameters and set the logfile name +# substitute template parameters and set the logfile name $GL_LOGT =~ s/%y/$y/g; $GL_LOGT =~ s/%m/$m/g; $GL_LOGT =~ s/%d/$d/g; diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 54448f6..da1822a 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -5,6 +5,15 @@ use warnings; use Data::Dumper; $Data::Dumper::Indent = 1; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Sortkeys = sub { return [ reverse sort keys %{$_[0]} ]; }; + # this is to make sure that $creater etc go to the end of the dumped hash. + # Without this, a setup that has something like + # @team = u1 u2 u3 + # repo priv/CREATER/.+ + # RW+ = CREATER + # RW = @team + # has a problem. The RW overrides the RW+ when the dumped hash is read in + # (simply going by sequence), so creater's special privs are lost # === add-auth-keys === @@ -352,6 +361,7 @@ wrap_chdir("$repo_base_abs"); for my $repo (sort keys %repos) { next unless $repo =~ $REPONAME_PATT; + print STDERR "creating $repo...\n"; unless (-d "$repo.git") { new_repo($repo, "$GL_ADMINDIR/src/hooks"); # new_repo would have chdir'd us away; come back diff --git a/src/hooks/update b/src/hooks/update index 235fa40..0ee7ad0 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -30,8 +30,21 @@ our %repos; # we should already have the GL_RC env var set when we enter this hook die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; -# then "do" the compiled config file, whose name we now know -die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; +# then "do" the compiled config file, whose name we now know. Before doing +# that we setup the creater etc from environment variables so that the parse +# interpolates them. We've minimised the duplication but this *does* +# duplicate a bit of parse_acl from gitolite.pm; we don't want to include that +# file here just for that little bit +{ + our $creater = $ENV{GL_CREATER}; + our $readers = $ENV{GL_READERS}; + our $writers = $ENV{GL_WRITERS}; + + die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; + + $repos{$ENV{GL_REPO}} = $repos{$ENV{GL_REPOPATT}} if ( $ENV{GL_REPOPATT} ); +} +my $reported_repo = $ENV{GL_REPO} . ( $ENV{GL_REPOPATT} ? " ($ENV{GL_REPOPATT})" : "" ); # ---------------------------------------------------------------------------- # start... @@ -101,12 +114,12 @@ sub check_ref { $refex = (keys %$ar)[0]; # refex? sure -- a regex to match a ref against :) next unless $ref =~ /$refex/; - die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; + die "$perm $ref $reported_repo $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; # as far as *this* ref is concerned we're ok return $refex if ($ar->{$refex} =~ /\Q$perm/); } - die "$perm $ref $ENV{GL_REPO} $ENV{GL_USER} DENIED by fallthru\n"; + die "$perm $ref $reported_repo $ENV{GL_USER} DENIED by fallthru\n"; } # and in this version, we have many "refs" to check. The one we print in the @@ -123,6 +136,6 @@ check_ref($_) for @refs; open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n"; print $log_fh "$ENV{GL_TS} $perm\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . - "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\t$log_refex\n"; + "\t$reported_repo\t$ref\t$ENV{GL_USER}\t$log_refex\n"; close $log_fh or die "close log failed: $!\n"; exit 0; From 6214ad3ab6a8d94dbbe3454afe1beda0d583c89d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 6 Dec 2009 11:41:02 +0530 Subject: [PATCH 171/850] wildrepos: first cut documentation --- conf/example.conf | 5 + doc/3-faq-tips-etc.mkd | 8 +- doc/4-wildcard-repositories.mkd | 207 ++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 doc/4-wildcard-repositories.mkd diff --git a/conf/example.conf b/conf/example.conf index b5ead17..cf52e46 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -56,6 +56,11 @@ # "@interns" now has 3 names in it, but note that this does # not change @staff +# WILDCARD REPOSITORIES ("wildrepos" BRANCH ONLY) +# ----------------------------------------- + +# Please see doc/4-wildcard-repositories.mkd for details + # REPO AND BRANCH PERMISSIONS # --------------------------- diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 464a4e5..c5e85e8 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -19,6 +19,7 @@ In this document: * what repos do I have access to? * "exclude" (or "deny") rules * "personal" branches + * repos named with wildcards * design choices * keeping the parser and the access control separate @@ -90,7 +91,7 @@ plain "git archive", because the Makefile adds a file called git clone git://sitaramc.indefero.net/sitaramc/gitolite.git cd gitolite make master.tar - # or maybe "make rebel.tar" or "make pu.tar" + # or maybe "make wildrepos.tar" or "make pu.tar" @@ -506,6 +507,11 @@ first check: Just don't *show* the user this config file; it might sound insulting :-) +#### repos named with wildcards + +**This feature only exists in the "wildrepos" branch!** Please see +`doc/4-wildcard-repositories.mkd` for all the details. + ### design choices #### keeping the parser and the access control separate diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd new file mode 100644 index 0000000..07cc9a8 --- /dev/null +++ b/doc/4-wildcard-repositories.mkd @@ -0,0 +1,207 @@ +# repositories named with wildcards + +***IMPORTANT NOTE***: + +This branch contains features that are likely to be much more brittle than the +"master" branch. Creating repositories based on wild cards, giving +"ownership" to the specific user who created it, allowing him/her to hand out +R and RW permissions to other users to collaborate, all these are possible. +And any of these could have a bug in it. + +---- + +In this document: + + * wildcard repos + * wildcard repos with creater name in them + * wildcard repos without creater name in them + * side-note: line-anchored regexes + * contrast with refexes + * handing out rights to wildcard-matached repos + * reporting + * other issues and discussion + +This document is mostly "by example". + +---- + +### Wildcard repos + +Which of these alternatives you choose depends on your needs, and the social +aspects of your environment. The first one is a little more rigid, making it +harder to make mistakes, and the second is more flexible and trusting. + +#### Wildcard repos with creater name in them + +Here's an example snippet: + + @prof = u1 + @TAs = u2 u3 + @students = u4 u5 u6 + + repo assignments/CREATER/a[0-9][0-9] + C = @students + RW+ = CREATER + RW = WRITERS @TAs + R = READERS @prof + +For now, ignore the special usernames READERS and WRITERS, and just create a +new repo, as user "u4" (a student): + + $ git clone git@server:assignments/u4/a12 + Initialized empty Git repository in /home/sitaram/t/a12/.git/ + Initialized empty Git repository in /home/gitolite/repositories/assignments/u4/a12.git/ + warning: You appear to have cloned an empty repository. + +Notice the *two* empty repo inits, and the order in which they occur ;-) Now +make some changes and push, and after that, that specific repo +(`assignments/u4/a12`) behaves as if the access control looked like this: + + # effective config + repo assignments/u4/a12 + RW+ = u4 + RW = WRITERS @TAs + R = READERS @prof + +#### Wildcard repos without creater name in them + +Here's how the same example would look if you did not want the CREATER's name +to be part of the actual repo name. + + repo assignments/a[0-9][0-9] + C = @students + RW+ = CREATER + RW = WRITERS @TAs + R = READERS @prof + +We haven't changed anything except the repo name pattern. This means that the +first student that creates, say, `assignments/a12` becomes the owner. +Mistakes (such as claiming a12 instead of a13) need to be rectified by an +admin logging on to the back end, though it's not too difficult. + +You could also repace the C line like this: + + C = @TAs + +and have a TA create the repos in advance. + +In either case, they could then use the (currently not implemented) `setperms` +feature to specify which users are "READERS" and which are "WRITERS". See +later for details. + +### Side-note: Line-anchored regexes + +A regex like + + repo assignments/S[0-9]+/A[0-9]+ + +would match `assignments/S02/A37`. It will not match `assignments/S02/ABC`, +or `assignments/S02/a37`, obviously. + +But you may be surprised to find that it does not match even +`assignments/S02/A37/B99`. This is because internally, gitolite +*line-anchors* the given regex; so that regex actually becomes +`^assignments/S[0-9]+/A[0-9]+$` -- notice the line beginning and ending +metacharacters. + +#### Contrast with refexes + +Just for interest, note that this is in contrast to the refexes for the normal +"branch" permissions, as described in `conf/example.conf` and elsewhere. +Those "refexes" are *not* anchored; a pattern like `refs/heads/master` +actually matches `foo/refs/heads/master01/bar` as well, even if no one will +actually push such a branch! You can anchor it if you really care, by using +`master$` instead of `master`, but anchoring is *not* the default for +refexes.] + +### Handing out rights to wildcard-matached repos + +***not yet implemented*** + +In the examples above, we saw two special "user" names: READERS and WRITERS. +The permissions they have are controlled by the config file, but ***who is +part of this list*** is controlled by the person who created the repository. + +The use case is that, although our toy example has only 3 students, in reality +there will be a few dozen, but each assignment will be worked on only by a +handful from among those. This allows the creater to take ad hoc sets of +users from among the actual users in the system, and place them into one of +two categories (whose permissions are, in this example, R and RW +respectively). In theory you could do the same thing by creating lots of +little "assignment-NN" groups in the config file but that may be a little too +cumbersome for non-secret environments. + +Create a small text file that contains the permissions you desire: + + $ cat > myperms + R user1 user3 + RW user2 + (hit ctrl-d here) + +...and use the new "getperms" command to set permissions for your repo: + + $ ssh git@server setperms XXX/XXX/XXX < myperms + New perms are: + R user1 user3 + RW user2 + +'setperms' will helpfully print what the new permissions are but you can also +use 'getperms' to check: + + $ ssh git@server getperms XXX/XXX/XXX + R user1 user3 + RW user2 + +The following points are important: + + * note the syntax of the commands; it's not a "git" command,and there's no + `:` like in a repo URL. The first space-separated word is R or RW, and + the rest are simple usernames. + + * whoever you specify as "R" will match the special user READERS. "RW" will + match WRITERS. + +### Reporting + +Remember the cool stuff you see when you just do `ssh git@server` (grep for +"myrights" in `doc/3-faq-tips-etc.mkd` if you forgot, or go [here][mr]). + +[mr]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#myrights + +This still works, except the format is a little more compressed to accommodate +a new column (at the start) for "C" permissions, which indicate that you are +allowed to *create* repos matching that pattern. + +In addition, there's a second level of reporting now, which is used to find +what *actual* repos are available when you supply a pattern. + + XXX to be done XXX + +### Other issues and discussion + + * *what if the repo name being pushed matches more than one pattern*? + + I think it would be very hard to reason about access if we were to do + something like combine all the access rights in all the matching patterns. + No matter how you do it, and how carefully you document it, there'll be + someone who is surprised by the result. + + And in security, that's a ***Bad Thing***. + + So we don't combine permissions. At runtime, we die if we find more than + one match. Let 'em go holler at the admin for creating multiple matching + repo patterns :-) + + This can make some repos inaccessible if the patterns changed *after* they + were created. The administrator should be careful not to do this. Most + of the time, it won't be difficult; the fixed prefix will usually be + different anyway so there won't be overlaps. + +---- + +Enjoy, and please use with care. This is pretty powerful stuff. As they say: +if you break it, you get to keep both pieces :) + +[jwzq]: http://regex.info/blog/2006-09-15/247 + +[av]: http://en.wikipedia.org/wiki/Autovivification From f620044156b1e80354b45ac0d8934bed5da1c098 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 6 Dec 2009 14:39:40 +0530 Subject: [PATCH 172/850] wildrepos: implement getperms and setperms --- src/gitolite.pm | 20 ++++++++++++++++++++ src/gl-auth-command | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index f341ab1..4507f54 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -123,6 +123,26 @@ sub repo_rights return ($c, $r, $w); } +# ---------------------------------------------------------------------------- +# getperms and setperms +# ---------------------------------------------------------------------------- + +sub get_set_perms +{ + my($repo_base_abs, $repo, $verb, $user) = @_; + my ($creater, $dummy, $dummy2) = &repo_rights($repo_base_abs, $repo, ""); + die "$repo doesnt exist or is not yours\n" unless $user eq $creater; + wrap_chdir("$repo_base_abs"); + wrap_chdir("$repo.git"); + if ($verb eq 'getperms') { + print STDERR `cat gl-perms 2>/dev/null`; + } else { + system("cat > gl-perms"); + print STDERR "New perms are:\n"; + print STDERR `cat gl-perms`; + } +} + # ---------------------------------------------------------------------------- # parse the compiled acl # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index eff0d77..d74b20d 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -60,6 +60,36 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { } my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; +my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); + +# ---------------------------------------------------------------------------- +# get and set perms for actual repo created by wildcard-autoviv +# ---------------------------------------------------------------------------- + +my $CUSTOM_COMMANDS=qr/^\s*(expand|getperms|setperms)\s/; + +# note that all the subs called here chdir somewhere else and do not come +# back; they all blithely take advantage of the fact that processing custom +# commands is sort of a dead end for normal (git) processing + +if ($cmd =~ $CUSTOM_COMMANDS) { + my ($verb, $repo) = ($cmd =~ /^\s*(\S+)\s+\/?(.*?)(?:.git)?$/); + if ($repo =~ $REPONAME_PATT and $verb =~ /getperms|setperms/) { + # with an actual reponame, you can "getperms" or "setperms" + get_set_perms($repo_base_abs, $repo, $verb, $user); + } + elsif ($repo !~ $REPONAME_PATT and $verb eq 'expand') { + # with a wildcard, you can "expand" it to see what repos actually match + die "not implemented yet\n"; + } else { + die "$cmd doesn't make sense to me\n"; + } + exit 1; +} + +# ---------------------------------------------------------------------------- +# normal (git) processing +# ---------------------------------------------------------------------------- # split into command and arguments; the pattern allows old style as well as # new style: "git-subcommand arg" or "git subcommand arg", just like gitosis @@ -78,8 +108,6 @@ die "bad command: $cmd. Make sure the repo name is exactly as in your config\n" # first level permissions check # ---------------------------------------------------------------------------- -my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); - if ( -d "$repo_base_abs/$repo.git" ) { # existing repo my ($creater, $user_R, $user_W) = &repo_rights($repo_base_abs, $repo, $user); From 02cee1d5cf513f21023a223b554b05903cf027da Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 6 Dec 2009 15:26:53 +0530 Subject: [PATCH 173/850] wildrepos: expanded access reporting This feature has *no* warranty, and so no documentation. Not more than this transcript anyway. config file: @prof = u1 @TAs = u2 u3 @students = u4 u5 u6 repo assignments/CREATER/a[0-9][0-9] C = @students RW+ = CREATER RW = WRITERS @TAs R = READERS @prof session: as user "u4": # check your permissions $ ssh git@server PTY allocation request failed on channel 0 hello u4, the gitolite version here is v0.95-31-gbcb14ca you have the following permissions: C assignments/CREATER/a[0-9][0-9] @ @ testing Connection to localhost closed. # autovivify repos for assignment 12 and 24 $ git clone git@server:assignments/u4/a12 a12 Initialized empty Git repository in /home/sitaram/t/a12/.git/ Initialized empty Git repository in /home/gitolite/repositories/assignments/u4/a12.git/ warning: You appear to have cloned an empty repository. $ git clone git@server:assignments/u4/a24 a24 Initialized empty Git repository in /home/sitaram/t/a24/.git/ Initialized empty Git repository in /home/gitolite/repositories/assignments/u4/a24.git/ warning: You appear to have cloned an empty repository. # check what repos you autovivified $ ssh git@server expand assignments/u4/a[0-9][0-9] (u4) assignments/u4/a12 (u4) assignments/u4/a24 as user "u5": # check your basic permissions $ ssh git@server PTY allocation request failed on channel 0 hello u5, the gitolite version here is v0.95-31-gbcb14ca you have the following permissions: C assignments/CREATER/a[0-9][0-9] @ @ testing Connection to localhost closed. # see if you have access to any of u4's repos $ ssh git@server expand assignments/u4/a[0-9][0-9] # (no output produced) as user "u4": # allow "u5" read access to assignment 12 # note you type in "R u5", hit enter, then hit Ctrl-D. Gitolite # then produces a confirmation message starting "New perms are:" $ ssh git@server setperms assignments/u4/a12 R u5 New perms are: R u5 as user "u5": # again see if you have access to any u4 repos $ ssh git@server expand assignments/u4/a[0-9][0-9] (u4) assignments/u4/a12 as user "u4": # check what permissions you gave to assignment 12 $ ssh git@server getperms assignments/u4/a12 R u5 # add RW access to "u6" to assignment 12 # again, type 'em in, then hit Ctrl-D; and note each time you run # this you're starting from scratch -- you can't "add to" the # permissions. Deal with it... $ ssh git@server setperms assignments/u4/a12 R u5 RW u6 New perms are: R u5 RW u6 as user "u6": # check what u4 stuff you have access to $ ssh git@server expand assignments/u4/a[0-9][0-9] (u4) assignments/u4/a12 --- doc/4-wildcard-repositories.mkd | 21 ++++++++----------- src/gitolite.pm | 36 ++++++++++++++++++++++++++++++--- src/gl-auth-command | 2 +- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 07cc9a8..a4c1f75 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -134,23 +134,23 @@ cumbersome for non-secret environments. Create a small text file that contains the permissions you desire: $ cat > myperms - R user1 user3 - RW user2 + R u5 + RW u6 (hit ctrl-d here) ...and use the new "getperms" command to set permissions for your repo: - $ ssh git@server setperms XXX/XXX/XXX < myperms + $ ssh git@server setperms assignments/u4/a12 < myperms New perms are: - R user1 user3 - RW user2 + R u5 + RW u6 'setperms' will helpfully print what the new permissions are but you can also use 'getperms' to check: - $ ssh git@server getperms XXX/XXX/XXX - R user1 user3 - RW user2 + $ ssh git@server getperms assignments/u4/a12 + R u5 + RW u6 The following points are important: @@ -172,11 +172,6 @@ This still works, except the format is a little more compressed to accommodate a new column (at the start) for "C" permissions, which indicate that you are allowed to *create* repos matching that pattern. -In addition, there's a second level of reporting now, which is used to find -what *actual* repos are available when you supply a pattern. - - XXX to be done XXX - ### Other issues and discussion * *what if the repo name being pushed matches more than one pattern*? diff --git a/src/gitolite.pm b/src/gitolite.pm index 4507f54..41c45b1 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -156,8 +156,8 @@ sub parse_acl # void $r if same as $w (otherwise "readers" overrides "writers"; this is # the same problem that needed a sort sub for the Dumper in the compile - # script, but localised to just $readers and $writers) - $r = "" if $r eq $w; + # script, but in this case it's limited to just $readers and $writers) + $r = "NOBODY" if $r eq $w; # set up the variables for a parse to interpolate stuff from the dumped # hash (remember the selective conversion of single to double quotes?). @@ -209,5 +209,35 @@ sub report_basic print "$perm\t$r\n\r" if $perm =~ /\S/; } } -1; +# ---------------------------------------------------------------------------- +# print a report of $user's basic permissions +# ---------------------------------------------------------------------------- + +sub expand_wild +{ + my($GL_CONF_COMPILED, $repo_base_abs, $repo, $user) = @_; + + # display matching repos (from *all* the repos in the system) that $user + # has at least "R" access to + + chdir("$repo_base_abs") or die "chdir $repo_base_abs failed: $!\n"; + for my $actual_repo (`find . -type d -name "*.git"|sort`) { + chomp ($actual_repo); + $actual_repo =~ s/^\.\///; + $actual_repo =~ s/\.git$//; + # it has to match the pattern being expanded + next unless $actual_repo =~ /^$repo$/; + + # find the creater and subsitute in repos + my ($creater, $read, $write) = &repo_rights($repo_base_abs, $actual_repo, $user); + # get access list with this + &parse_acl($GL_CONF_COMPILED, "", $creater, $read || "NOBODY", $write || "NOBODY"); + + # you need a minimum of "R" access to the regex we're talking about + next unless $repos{$repo}{R}{'@all'} or $repos{$repo}{R}{$user}; + print STDERR "($creater)\t$actual_repo\n"; + } +} + +1; diff --git a/src/gl-auth-command b/src/gl-auth-command index d74b20d..cc4b5f9 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -80,7 +80,7 @@ if ($cmd =~ $CUSTOM_COMMANDS) { } elsif ($repo !~ $REPONAME_PATT and $verb eq 'expand') { # with a wildcard, you can "expand" it to see what repos actually match - die "not implemented yet\n"; + expand_wild($GL_CONF_COMPILED, $repo_base_abs, $repo, $user); } else { die "$cmd doesn't make sense to me\n"; } From 135079c9d753f354ae350eb42146e7e5ea0ad9eb Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 6 Dec 2009 20:40:23 +0530 Subject: [PATCH 174/850] minor docfix --- doc/4-wildcard-repositories.mkd | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index a4c1f75..7e54a1c 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -85,9 +85,8 @@ You could also repace the C line like this: and have a TA create the repos in advance. -In either case, they could then use the (currently not implemented) `setperms` -feature to specify which users are "READERS" and which are "WRITERS". See -later for details. +In either case, they could then use the `setperms` feature to specify which +users are "READERS" and which are "WRITERS". See later for details. ### Side-note: Line-anchored regexes @@ -116,8 +115,6 @@ refexes.] ### Handing out rights to wildcard-matached repos -***not yet implemented*** - In the examples above, we saw two special "user" names: READERS and WRITERS. The permissions they have are controlled by the config file, but ***who is part of this list*** is controlled by the person who created the repository. From 5416e38ea8e11ed856386e43c759da6ab3958a7d Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Fri, 4 Dec 2009 17:04:40 +0200 Subject: [PATCH 175/850] Fix default configuration paths in documentation Signed-off-by: Teemu Matilainen --- src/gl-compile-conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 56b11ed..6dfc050 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -27,11 +27,11 @@ $Data::Dumper::Sortkeys = 1; # - anytime a pubkey is added/deleted # - anytime gitolite.conf is changed # input: -# - GL_CONF (default: ~/.gitolite/gitolite.conf) +# - GL_CONF (default: ~/.gitolite/conf/gitolite.conf) # - GL_KEYDIR (default: ~/.gitolite/keydir) # output: # - ~/.ssh/authorized_keys (dictated by sshd) -# - GL_CONF_COMPILED (default: ~/.gitolite/gitolite.conf-compiled.pm) +# - GL_CONF_COMPILED (default: ~/.gitolite/conf/gitolite.conf-compiled.pm) # security: # - touches a very critical system file that manages the restrictions on # incoming users. Be sure to audit AUTH_COMMAND and AUTH_OPTIONS (see From cdb7245d44a57ec73ebac50f073eef1388bec15b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 8 Dec 2009 13:51:11 +0530 Subject: [PATCH 176/850] example conf: clarify what @all means ...thanks to Grum for catching this --- conf/example.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/example.conf b/conf/example.conf index b5ead17..3768667 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -82,7 +82,8 @@ repo gitolite-admin RW+ = @admins - # "@all" is a special, predefined, group name + # "@all" is a special, predefined, group name of all users + # (everyone who has a pubkey in keydir) repo testing RW+ = @all From 4441ed82e4967b08ce18018ee7d6467bab3b689b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 8 Dec 2009 15:03:38 +0530 Subject: [PATCH 177/850] compile: allow full email addresses as usernames we had usurped the email style syntax to separate multiple keys belonging to the same person, like sitaram@desktop.pub and sitaram@laptop.pub. If you have so many users that you need the full email address to disambiguate some of them (or you want to do it for just plain convenience), you couldn't. This patch fixes that in a backward compatible way. See doc/3-faq-tips-etc.mkd for details. --- conf/example.conf | 10 +++++++--- doc/3-faq-tips-etc.mkd | 28 ++++++++++++++++++++++++++-- src/gitolite.pm | 6 +++--- src/gl-compile-conf | 7 +++---- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index b5ead17..b2173e3 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -6,9 +6,13 @@ # the description string for gitweb) # - comments in the normal shell-ish style; no surprises there # - there are NO continuation lines of any kind -# - user/repo names as simple as possible -# (usernames: only alphanumerics, ".", "_", "-"; -# reponames: same, plus "/", but not at the start) +# - user/repo names as simple as possible; they must start with an +# alphanumeric, but after that they can also contain ".", "_", "-". +# - usernames can optionally be followed by an "@" and a domainname +# containing at least one "." (this allows you to use an email +# address as someone's username) +# - reponames can contain "/" characters (this allows you to +# put your repos in a tree-structure for convenience) # objectives, over and above gitosis: # - simpler syntax diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 464a4e5..9a1d413 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -344,12 +344,36 @@ gitolite knows these two keys belong to the same person. Note that you don't say "sitaram@laptop" and so on in the **config** file -- as far as the config file is concerned there's just **one** user called -"sitaram" -- so you only say "sitaram" there. Only the **pubkey files** have -the extra "@" stuff. +"sitaram" -- so you only say "sitaram" there. I think this is easier to maintain if you have to delete or change one of those keys. +However, now that `sitaramc@gmail.com` is also a valid username, we need to +distinguish between `sitaramc@gmail.com.pub` and `sitaramc@desktop.pub`. We +do that by requiring that the multi-key suffix you use (like "desktop" and +"laptop") should not have a `"."` in it. If it does, it looks like an email +address. The following table lists sample pubkey filenames and the +corresponding derived usernames (which is what goes into the +`conf/gitolite.conf` file): + + * old style multikeys; not mistaken for emails because there is no "." in + hostname part + + sitaramc.pub sitaramc + sitaramc@laptop.pub sitaramc + sitaramc@desktop.pub sitaramc + + * new style, email keys; there is a "." in hostname part; so it's an email + address + + sitaramc@gmail.com.pub sitaramc@gmail.com + + * multikeys *with* email address + + sitaramc@gmail.com@laptop.pub sitaramc@gmail.com + sitaramc@gmail.com@desktop.pub sitaramc@gmail.com + #### support for git installed outside default PATH The normal solution is to add to the system default PATH somehow, either by diff --git a/src/gitolite.pm b/src/gitolite.pm index 71c0b0c..ee0fc77 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -24,9 +24,9 @@ $WARN = "\n\t\t***** WARNING *****\n "; $R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/; $W_COMMANDS=qr/^git[ -]receive-pack$/; -# note that REPONAME_PATT allows a "/" also, which USERNAME_PATT doesn't -$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern -$USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern +# note that REPONAME_PATT allows "/", while USERNAME_PATT allows "@" +$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern +$USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@-]*$); # very simple pattern # ---------------------------------------------------------------------------- # convenience subs diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 56b11ed..1125add 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -121,7 +121,7 @@ sub expand_list { # we test with the slightly more relaxed pattern here; we'll catch the # "/" in user name thing later; it doesn't affect security anyway - die "$ABRT bad user or repo name $item\n" unless $item =~ $REPONAME_PATT; + die "$ABRT bad user or repo name $item\n" unless $item =~ $REPONAME_PATT or $item =~ $USERNAME_PATT; if ($item =~ /^@/) # nested group { die "$ABRT undefined group $item\n" unless $groups{$item}; @@ -174,7 +174,6 @@ sub parse_conf_file # store the members of each group as hash key. Keep track of when # the group was *first* created by using $fragment as the *value* do { $groups{$1}{$_} ||= $fragment } for ( expand_list( split(' ', $2) ) ); - # again, we take the more "relaxed" pattern die "$ABRT bad group $1\n" unless $1 =~ $REPONAME_PATT; } # repo(s) @@ -200,7 +199,7 @@ sub parse_conf_file # expand the user list, unless it is just "@all" @users = expand_list ( @users ) unless (@users == 1 and $users[0] eq '@all'); - do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; + do { die "$ABRT bad username $_ PATT is $USERNAME_PATT,\n" unless $_ =~ $USERNAME_PATT } for @users; # ok, we can finally populate the %repos hash for my $repo (@repos) # each repo in the current stanza @@ -408,7 +407,7 @@ for my $pubkey (glob("*")) print STDERR "WARNING: pubkey files should end with \".pub\", ignoring $pubkey\n"; next; } - my $user = $pubkey; $user =~ s/(\@.+)?\.pub$//; + my $user = $pubkey; $user =~ s/(\@[^.]+)?\.pub$//; # lint check 2 print STDERR "WARNING: pubkey $pubkey exists but user $user not in config\n" unless $user_list{$user}; From 3403d40d0e0a478252fa4111a44fb42218f92f15 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Mon, 7 Dec 2009 22:20:29 +0200 Subject: [PATCH 178/850] Add support for repo configurations Git repository configurations can be set/unset by declaring "config" lines in "repo" stanzas in gitolite.conf. For example: repo gitolite config hooks.mailinglist = gitolite-commits@example.tld config hooks.emailprefix = "[gitolite] " config foo.bar = "" config foo.baz = The firs two set (override) the values. Double quotes must be used to preserve preceding spaces. Third one sets an empty value and the last removes all keys. Signed-off-by: Teemu Matilainen --- src/gl-compile-conf | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 7526da5..138249d 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -101,6 +101,9 @@ my %rurp_seen = (); # catch usernames<->pubkeys mismatches; search for "lint" below my %user_list = (); +# repo configurations +my %repo_config = (); + # gitweb descriptions and owners; plain text, keyed by "$repo.git" my %desc = (); my %owner = (); @@ -244,6 +247,16 @@ sub parse_conf_file } } } + # configuration + elsif (/^config (.+) = ?(.*)/) + { + my ($key, $value) = ($1, $2); + die "$WARN $fragment attempting to set repo configuration\n" if $fragment ne 'master'; + for my $repo (@repos) # each repo in the current stanza + { + $repo_config{$repo}{$key} = $value; + } + } # very simple syntax for the gitweb description of repo; one of: # reponame = "some description string" # reponame "owner name" = "some description string" @@ -338,6 +351,22 @@ warn "\n\t\t***** WARNING *****\n" . "\t\"git version dependency\" in doc/3-faq-tips-etc.mkd\n" if $git_version < 10602; # that's 1.6.2 to you +# ---------------------------------------------------------------------------- +# update repo configurations +# ---------------------------------------------------------------------------- + +for my $repo (keys %repo_config) { + wrap_chdir("$repo_base_abs/$repo.git"); + while ( my ($key, $value) = each(%{ $repo_config{$repo} }) ) { + if ($value) { + $value =~ s/^"(.*)"$/$1/; + system("git", "config", $key, $value); + } else { + system("git", "config", "--unset-all", $key); + } + } +} + # ---------------------------------------------------------------------------- # handle gitweb and daemon # ---------------------------------------------------------------------------- From 64979c18ea2758f87960965bf325d310ba1611cf Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 9 Dec 2009 12:16:22 +0530 Subject: [PATCH 179/850] document repo config support --- conf/example.conf | 23 +++++++++++++++++++++++ doc/2-admin.mkd | 23 +++++++++++++++++++++++ doc/3-faq-tips-etc.mkd | 7 +++++++ 3 files changed, 53 insertions(+) diff --git a/conf/example.conf b/conf/example.conf index 76db973..84ccff2 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -181,3 +181,26 @@ repo linux perl # give gitweb access as described above if you're specifying a description gitolite "Sitaram Chamarty" = "fast, secure, access control for git in a corporate environment" + +# REPO SPECIFIC GITCONFIG +# ----------------------- + +# (Thanks to teemu dot matilainen at iki dot fi) + +# this should be specified within a "repo" stanza + +# syntax: +# config sectionname.keyname = [optional value_string] + +# example usage: if you placed a hook in src/hooks that requires configuration +# information that is specific to each repo, you could do this: + +repo gitolite + config hooks.mailinglist = gitolite-commits@example.tld + config hooks.emailprefix = "[gitolite] " + config foo.bar = "" + config foo.baz = + +# This does either a plain "git config section.key value" (for the first 3 +# examples above) or "git config --unset-all section.key" (for the last +# example). Other forms (--add, the value_regex, etc) are not supported. diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index d0460ec..a93df4b 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -9,6 +9,7 @@ In this document: * adding users and repos * specifying gitweb and daemon access * custom hooks + * custom git config ### administer @@ -90,3 +91,25 @@ just run easy install once again; it'll do it to existing repos also. implements all the branch-level permissions in gitolite. If you fiddle with the hooks directory, please make sure you do not mess with this file accidentally, or all your fancy per-branch permissions will stop working.** + +#### custom git config + +The custom hooks feature is a blunt instrument -- all repos get the hook you +specified and will run it. In order to make it a little more fine-grained, +you could set your hooks to only work if a certain "gitconfig" variable was +set. Which means we now need a way to specify "git config" settings on a per +repository basis. + +Thanks to Teemu (teemu dot matilainen at iki dot fi), gitolite now does this +very easily. For security reasons, this can only be done from the master +config file (i.e., if you're using delegation, the delegated admins cannot +specify git config settings). + +Please see `conf/example.conf` for syntax. Note that this only supports the +basic forms of the "git config" command: + + git config section.key value # value may be an empty string + git config --unset-all section.key + +It does not (currently) support other options like `--add`, the `value_regex`, +etc. diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 9a1d413..b824dec 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -19,6 +19,7 @@ In this document: * what repos do I have access to? * "exclude" (or "deny") rules * "personal" branches + * custom hooks and custom git config * design choices * keeping the parser and the access control separate @@ -530,6 +531,12 @@ first check: Just don't *show* the user this config file; it might sound insulting :-) +#### custom hooks and custom git config + +You can specify hooks that you want to propagate to all repos, as well as +per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and +`conf/example.conf` for details. + ### design choices #### keeping the parser and the access control separate From 780b4cca200cdee3c761215e4c949f2432fc8897 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 10 Dec 2009 17:07:46 +0530 Subject: [PATCH 180/850] ssh-copy-id workaround detail plus a couple other doc fixes --- doc/0-INSTALL.mkd | 13 ++++++++----- doc/3-faq-tips-etc.mkd | 44 +++++++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 106e415..af4022f 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -37,13 +37,16 @@ Assumptions/pre-requisites: * git is installed on that server (and so is perl) * you have a userid on that server * you have ssh-pubkey (**password-less**) login to that userid - * (if you have only password access, run `ssh-keygen -t rsa` to create a - new keypair if needed, then run `ssh-copy-id user@host`) + * if you have only password access, run `ssh-keygen -t rsa` to create a + new keypair if needed, then run `ssh-copy-id user@host`. If you do + not have `ssh-copy-id`, read doc/3-faq-tips-etc.mkd and look for + `ssh-copy-id` in that file for instructions * you have a clone or an archive of gitolite somewhere on your workstation + * if you don't have one, just run `git clone git://github.com/sitaramc/gitolite` -If so, just `cd` to that clone and run `src/gl-easy-install` and follow the -prompts! (Running it without any arguments shows you usage plus other useful -info). +Once you have all this, just `cd` to that clone and run `src/gl-easy-install` +and follow the prompts! (Running it without any arguments shows you usage +plus other useful info). #### typical example run diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index b824dec..b6c65d9 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -25,16 +25,25 @@ In this document: ### common errors and mistakes - * forgetting to suffix `.git` to the end of the reponame in the `git clone`. - This suffix is *not* used in the gitolite config file for the sake of - clarity and cleaner syntax, but don't let that fool you. It's a - convention in the git world that **bare repos** end with `.git`. - * adding `repositories/` at the start of the repo name in the `git clone`. This error is typically made by the *admin* himself -- because he knows what `$REPO_BASE` is set to and thinks he has to provide that prefix on - the client side also :-) In fact gitolite prepends `$REPO_BASE` when it - is required anyway, so you shouldn't do the same thing! + the client side also :-) In fact gitolite prepends `$REPO_BASE` + internally, so you shouldn't also do the same thing! + + * being able to clone but getting errors on push. Most likely caused by a + combination of: + + * you already have shell access to the server, not just "gitolite" + access, *and* + + * you cloned using `git clone git@server:repositories/repo.git` (notice + there's an extra "repositories/" in there?) + + In other words, you used a key that completely bypassed gitolite and went + straight to the shell to do the clone. + + Please see doc/6-ssh-troubleshooting.mkd for what all this means. ### git version dependency @@ -67,6 +76,27 @@ normal way, since it's not empty anymore. ### other errors, warnings, notes... + * don't have `ssh-copy-id`? This is broadly what that command does, if you + want to replicate it manually. The input is your pubkey, typically + `~/.ssh/id_rsa.pub` from your client/workstation. + + * it copies it to the server as some file + + * it appends that file to `~/.ssh/authorized_keys` on the server + (creating it if it doesn't already exist) + + * it then makes sure that all these files/directories have go-w perms + set (assuming user is "git"): + + /home/git/.ssh/authorized_keys + /home/git/.ssh + /home/git + + [Actually, sshd requires that even directories *above* ~ (/, /home, + typically) also must be `go-w`, but that needs root. And typically + they're already set that way anyway. (Or if they're not, you've got + bigger problems than gitolite install not working!)] + * cloning an empty repo is only possible with clients greater than 1.6.2. So at least one of your clients needs to have a recent git. Once at least one commit has been made, older clients can also use it From 33fc0a7e9fe98dac1eec284119cf47509d68ab8c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 11 Dec 2009 09:52:29 +0530 Subject: [PATCH 181/850] compile, parse_acl: treat foo/CREATER (no regex metas) correctly Teemu's testing brought up a situtation I had not anticipated: "repo foo/CREATER" looks like a non-regex, and its creation then (a) goes by "W" permissions instead of "C" permissions, and (b) the creater's name does not get recorded (no gl-creater file). SIDE NOTE: one way is to reduce the paranoia, and just put the creater name in anyway. Treat a repo created from gl-auth as a wildcard-matched autovivified repo, because the *other* kind would have actually got created by gl-compile anyway. However, I can think of *one* very far-out situation where this could backfire on an unwary admin, and I'm paranoid :-) So we need to force it to look like a regex. Moving the line-anchoring from `parse_acl` to gl-compile sounded fine, until I realised that the "$" isn't easy. Backslashitis, bigtime, plus the single/double quote tricks we're playing with the dumped hash adds its own complexities. Best of both worlds, promote the "^" to gl-compile, keep the "$" where it is...! --- src/gitolite.pm | 8 +++++++- src/gl-compile-conf | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 41c45b1..0534a87 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -177,9 +177,15 @@ sub parse_acl return unless $repo; return $ENV{GL_REPOPATT} = "" if $repos{$repo}; - my @matched = grep { $repo =~ /^$_$/ } sort keys %repos; + + # didn't find $repo in %repos, so it must be a wildcard-match case + + # note that the repo regexes in %repos have a leading ^ but not a trailing + # $; we need to add the $ here to complete the "line-anchoring" + my @matched = grep { $repo =~ /$_$/ } sort keys %repos; die "$repo has no matches\n" unless @matched; die "$repo has multiple matches\n@matched\n" if @matched > 1; + # found exactly one pattern that matched, copy its ACL $repos{$repo} = $repos{$matched[0]}; # and return the pattern diff --git a/src/gl-compile-conf b/src/gl-compile-conf index da1822a..618dcae 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -192,7 +192,11 @@ sub parse_conf_file @repos = split ' ', $1; @repos = expand_list ( @repos ); - s/\bCREAT[EO]R\b/\$creater/g for @repos; + # CREAT[EO]R must be changed to $creater. Also, prefix a "^" to + # force it to look like a regex. Otherwise, foo/CREATER/bar (no + # regex metas) looks like an ordinary reponame, and the logic (in + # gl-auth) that decides when to allow autovivify gets confused. + s/\bCREAT[EO]R\b/\$creater/g && s/^/^/ for @repos; } # actual permission line elsif (/^(-|C|R|RW|RW\+) (.* )?= (.+)/) From a15e910cf8b0ff305f512cddcb64cbf76f4132b1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 11 Dec 2009 21:37:33 +0530 Subject: [PATCH 182/850] auth: dont allow trailing slash in reponames... ...like "git clone host:foo/", even if it matches "repo foo/.*" NOTE: I expect a few more of these special cases to be found as time goes on and people find new ways to abuse the regex system, whether it is done intentionally or not. Anything not fixable by changing the config file will be fixed in the code asap. This one, for instance, seems fixable by using "foo/.+" instead of "foo/.*". But it actually isn't; the user can do "git clone host:foo//" and bypass that :( Still I suspect most situations will get an entry in the "then don't do that" file :) ---- patient: "doc, it hurts when I do this" doc: "then don't do that" --- src/gl-auth-command | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gl-auth-command b/src/gl-auth-command index cc4b5f9..3f0ea3b 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -103,6 +103,7 @@ my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:.git)?'/); die "bad command: $cmd. Make sure the repo name is exactly as in your config\n" unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ); +die "$repo ends with a slash; I don't like that\n" if $repo =~ /\/$/; # ---------------------------------------------------------------------------- # first level permissions check From 3eb29e17fc6a1fe6b79c31192e09d8d084f4def8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 12 Dec 2009 09:51:29 +0530 Subject: [PATCH 183/850] allow @ in repo names and patterns stuff like "repo foo/CREATER/.+" means the reponame has to be a superset of the username in terms of allowed characters --- src/gitolite.pm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 1ce0480..56336de 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -24,11 +24,13 @@ $WARN = "\n\t\t***** WARNING *****\n "; $R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/; $W_COMMANDS=qr/^git[ -]receive-pack$/; -# note that REPONAME_PATT allows "/", while USERNAME_PATT allows "@" -$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern +# note that REPONAME_PATT allows "/", while USERNAME_PATT does not +# also, the reason REPONAME_PATT is a superset of USERNAME_PATT is (duh!) +# because in this version, a repo can have "CREATER" in the name (see docs) +$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/-]*$); # very simple pattern $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@-]*$); # very simple pattern # same as REPONAME, plus some common regex metas -$REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._/-]*$); +$REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # ---------------------------------------------------------------------------- # convenience subs From ecdf6f2350e1d57d77d86ac5af6477e45bc25433 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 13 Dec 2009 12:43:44 +0530 Subject: [PATCH 184/850] auth: don't allow literal ".." in reponames thanks to Teemu for catching this --- src/gl-auth-command | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gl-auth-command b/src/gl-auth-command index 3f0ea3b..b9e6359 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -104,6 +104,7 @@ die "bad command: $cmd. Make sure the repo name is exactly as in your config\n" unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ); die "$repo ends with a slash; I don't like that\n" if $repo =~ /\/$/; +die "$repo has two consecutive periods; I don't like that\n" if $repo =~ /\.\./; # ---------------------------------------------------------------------------- # first level permissions check From ed2bf526f82eb2d281fb83e8126580b78223fa94 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 13 Dec 2009 19:17:18 +0530 Subject: [PATCH 185/850] minor docfix --- doc/3-faq-tips-etc.mkd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index b6c65d9..0e3af99 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -101,10 +101,10 @@ normal way, since it's not empty anymore. So at least one of your clients needs to have a recent git. Once at least one commit has been made, older clients can also use it - * when you clone an empty repo, git seems to complain about the remote - hanging up or something. I have no idea what that is, but it doesn't seem - to hurt anything. This happens even in normal git, not just gitolite. - [Update 2009-09-14; this has been fixed in git 1.6.4.3] + * when you clone an empty repo, git seems to complain about `fatal: The + remote end hung up unexpectedly`. However, you can ignore this, since it + doesn't seem to hurt anything. [Update 2009-09-14; this has been fixed in + git 1.6.4.3] * gitweb not able to read your repos? You can change the umask for newly created repos to something more relaxed -- see the `~/.gitolite.rc` file From b7404aa772adee312483760da869497f6d2a64a2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 15 Dec 2009 12:35:48 +0530 Subject: [PATCH 186/850] auth/install/pu-hook: pass ADMINDIR and BINDIR via ENV The admin repo's post-update hook needs to know where $GL_ADMINDIR is, and we had a weird way of doing that which depended on gl-install actually munging the hook code. We also always assumed the binaries are in GL_ADMINDIR/src. We now use an env var to pass both these values. This removes the weird dependency on gl-install that the post-update hook had, as well as make running other programs easier due to the new $GL_BINDIR env var. --- src/ga-post-update-hook | 7 ++----- src/gl-auth-command | 5 +++++ src/gl-compile-conf | 2 +- src/gl-easy-install | 2 +- src/gl-install | 2 -- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ga-post-update-hook b/src/ga-post-update-hook index 8090b8a..91d2bfb 100755 --- a/src/ga-post-update-hook +++ b/src/ga-post-update-hook @@ -1,10 +1,7 @@ #!/bin/sh -# get this from your .gitolite.conf; and don't forget this is shell, while -# that is perl :-) -export GL_ADMINDIR; GL_ADMINDIR=$HOME/.gitolite - # checkout the master branch to $GL_ADMINDIR +# (the GL_ADMINDIR env var would have been set by gl-auth-command) GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master # remove all fragments. otherwise, you get spurious error messages when you @@ -31,4 +28,4 @@ do done cd $GL_ADMINDIR -src/gl-compile-conf +$GL_BINDIR/gl-compile-conf diff --git a/src/gl-auth-command b/src/gl-auth-command index 86fec88..12b3234 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -39,6 +39,11 @@ require "$bindir/gitolite.pm"; &where_is_rc(); die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; +# we need to pass GL_ADMINDIR and the bindir to the child hooks (well only the +# admin repo's post-update hook but still...) +$ENV{GL_ADMINDIR} = $GL_ADMINDIR; +$ENV{GL_BINDIR} = $bindir; + # add a custom path for git binaries, if specified $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 138249d..ab1155c 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -73,7 +73,7 @@ $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; # ---------------------------------------------------------------------------- # command and options for authorized_keys -$AUTH_COMMAND="$GL_ADMINDIR/src/gl-auth-command"; +$AUTH_COMMAND="$bindir/gl-auth-command"; $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; # groups can now represent user groups or repo groups. diff --git a/src/gl-easy-install b/src/gl-easy-install index db20e2f..d73c82d 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -48,7 +48,7 @@ main() { [[ $upgrade == 0 ]] && initial_conf_key # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" - ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf $quiet" + ssh -p $port $user@$host "cd $GL_ADMINDIR; \$PWD/src/gl-compile-conf $quiet" setup_pta diff --git a/src/gl-install b/src/gl-install index a4dc729..9494fc6 100755 --- a/src/gl-install +++ b/src/gl-install @@ -79,8 +79,6 @@ for my $repo (`find . -type d -name "*.git"`) { if ( -d "gitolite-admin.git/hooks" ) { print "copying post-update hook to gitolite-admin repo...\n"; system("cp $GL_ADMINDIR/src/ga-post-update-hook gitolite-admin.git/hooks/post-update"); - system("perl", "-i", "-p", "-e", "s(GL_ADMINDIR=.*)(GL_ADMINDIR=$GL_ADMINDIR)", - "gitolite-admin.git/hooks/post-update"); chmod 0755, "gitolite-admin.git/hooks/post-update"; } From 512fc4a0a547a3c29d1502e8caddd692ac880890 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 15 Dec 2009 16:11:21 +0530 Subject: [PATCH 187/850] auth: set umask when autoviv-ing repos Looks like I'd forgotten this when I did the autoviv code. Repos created via gl-compile (when you add a new repo to the config file and push) worked fine, but repos created via gl-auth (when you autoviv a repo, wild or not) did not. This *should* be merged into wildrepos soon after testing; wildrepos will have a lot more autoviv-ing than master. --- src/gl-auth-command | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 12b3234..463439e 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -23,9 +23,8 @@ use warnings; # common definitions # ---------------------------------------------------------------------------- - # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $GL_ADMINDIR); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT); our %repos; @@ -47,6 +46,9 @@ $ENV{GL_BINDIR} = $bindir; # add a custom path for git binaries, if specified $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; +# set the umask before creating any files +umask($REPO_UMASK); + # ---------------------------------------------------------------------------- # start... # ---------------------------------------------------------------------------- From b679bbb56bdc1a44a316298c9c3f7fb0d52f13dc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Dec 2009 10:15:35 +0530 Subject: [PATCH 188/850] allow '+' as valid character in user/reponames --- src/gitolite.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index ee0fc77..d905a72 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -25,8 +25,8 @@ $R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/; $W_COMMANDS=qr/^git[ -]receive-pack$/; # note that REPONAME_PATT allows "/", while USERNAME_PATT allows "@" -$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern -$USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@-]*$); # very simple pattern +$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/+-]*$); # very simple pattern +$USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple pattern # ---------------------------------------------------------------------------- # convenience subs From 75de6c0438090d0d4d4e593867739d638cb7f8d6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 19 Dec 2009 20:52:30 +0530 Subject: [PATCH 189/850] auth: (WDITOT?) allow special users to get a shell ".../gl-auth-command username" is the normal command that authkeys forces, and this prevents that key from being used to get a shell. We now allow the user to get a shell if the forced command has a "-s" before the "username", like ".../gl-auth-command -s sitaram". (Now that a plain "ssh gitolite" gets you a shell, there's a new "info" command that such privileged keys can use to get basic access info). Thanks to Jesse Keating for the idea! I can't believe this never occurred to me before, but I guess I was so enamoured of my "innovation" in converting what used to be an error into some useful info I didn't think a bit more :/ --- src/gl-auth-command | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 463439e..2de7d43 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -53,6 +53,14 @@ umask($REPO_UMASK); # start... # ---------------------------------------------------------------------------- +# if the first argument is a "-s", this user is allowed to get a shell using +# this key +my $shell_allowed = 0; +if ($ARGV[0] eq '-s') { + $shell_allowed = 1; + shift; +} + # first, fix the biggest gripe I have with gitosis, a 1-line change my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! @@ -60,13 +68,24 @@ my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! # sanity checks on SSH_ORIGINAL_COMMAND # ---------------------------------------------------------------------------- -# SSH_ORIGINAL_COMMAND must exist; if not, we die with a nice message +# print basic access info if SSH_ORIGINAL_COMMAND does not exist unless ($ENV{SSH_ORIGINAL_COMMAND}) { + # unless the user is allowed to use a shell + if ($shell_allowed) { + my $shell = $ENV{SHELL}; + $shell =~ s/.*\//-/; # change "/bin/bash" to "-bash" + exec { $ENV{SHELL} } $shell; + } &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); exit 1; } my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; +# people allowed to get a shell can get basic access info by asking nicely +if ($shell_allowed and $cmd eq 'info') { + &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); + exit 1; +} # split into command and arguments; the pattern allows old style as well as # new style: "git-subcommand arg" or "git subcommand arg", just like gitosis @@ -77,9 +96,12 @@ my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; # including the single quotes my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:.git)?'/); -die "bad command: $cmd. Make sure the repo name is exactly as in your config\n" - unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) - and $repo and $repo =~ $REPONAME_PATT ); +unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) { + # if the user is allowed a shell, just run the command + exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND} if $shell_allowed; + # otherwise, whine + die "bad command: $cmd\n"; +} # ---------------------------------------------------------------------------- # first level permissions check From 2cc19091ca53e38bf4458d6c39ca409a8c95979d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 19 Dec 2009 21:36:55 +0530 Subject: [PATCH 190/850] compile: gitolite key as good as shell key for users in @SHELL group done by inserting a "-s" into the authkey forced command. (They also lose the "no-pty" restriction, for good measure!) --- src/gl-compile-conf | 9 +++++++-- src/gl-easy-install | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index ab1155c..b8e8a1c 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -74,7 +74,8 @@ $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; # command and options for authorized_keys $AUTH_COMMAND="$bindir/gl-auth-command"; -$AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; +$AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding"; + # note, for most users there's also a "no-pty" added to this, see later # groups can now represent user groups or repo groups. @@ -441,7 +442,11 @@ for my $pubkey (glob("*")) print STDERR "WARNING: pubkey $pubkey exists but user $user not in config\n" unless $user_list{$user}; $user_list{$user} = 'has pubkey'; - print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; + if ($groups{'@SHELL'}{$user}) { + print $newkeys_fh "command=\"$AUTH_COMMAND -s $user\",$AUTH_OPTIONS "; + } else { + print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS,no-pty "; + } # apparently some pubkeys don't end in a newline... my $pubkey_content = `cat $pubkey`; $pubkey_content =~ s/\s*$/\n/; diff --git a/src/gl-easy-install b/src/gl-easy-install index d73c82d..b74621a 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -361,6 +361,7 @@ run_install() { # MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf # and add at least the following lines to it: +# @SHELL = sitaram # repo gitolite-admin # RW+ = sitaram @@ -368,6 +369,8 @@ initial_conf_key() { echo "#gitolite conf # please see conf/example.conf for details on syntax and features +@SHELL = $admin_name + repo gitolite-admin RW+ = $admin_name From 714e2142586069810f8a6e7abe4dac41c3fa4440 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 20 Dec 2009 11:57:53 +0530 Subject: [PATCH 191/850] wildrepos: catch-all disclaimer for missing features ;-) --- doc/4-wildcard-repositories.mkd | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 7e54a1c..1feac40 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -8,6 +8,14 @@ This branch contains features that are likely to be much more brittle than the R and RW permissions to other users to collaborate, all these are possible. And any of these could have a bug in it. +"Brittle" also means some features in "master" may not work here. For +example, you cannot specify gitconfig values for a wildcard repo; it only +works for actual repos. + +There may be other such missing features. Sometimes it's just not possible to +make it work. Or it may be cumbersome enough that unless there are *no* +workarounds I may not have the time to code it right away. + ---- In this document: From 6f45f75ca11a2711e03ff057c7cfbbddac9e65dd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 21 Dec 2009 06:23:25 +0530 Subject: [PATCH 192/850] minor docfix --- doc/6-ssh-troubleshooting.mkd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 2b9febc..4bf980f 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -148,8 +148,9 @@ Here's how it all hangs together. Luckily, ssh has a very convenient way of capturing all the connection information (username, hostname, port number (if it's not the default 22), - and keypair to be used) in one "paragraph". This is what the para looks - like for us (the easy install script puts it there the first time): + and keypair to be used) in one "paragraph" of `~/.ssh/config`. This is + what the para looks like for us (the easy install script puts it there the + first time): host gitolite user git From 981d693dec7e01fed4da502964b38df6101762a1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 21 Dec 2009 17:33:53 +0530 Subject: [PATCH 193/850] Revert "compile, parse_acl: treat foo/CREATER (no regex metas) correctly" This reverts commit 33fc0a7e9fe98dac1eec284119cf47509d68ab8c. Was causing too much trouble with access reporting (basic and expanded) because of the extra ^ at the start... The paranoia referred to in that commit was this sequence: - admin creates a named (non wildcard) repo using config file push - somehow that gets deleted (OS error, corruption, ...) - admin just asks anyone with a current repo to push it to auto-revive it (because we allow people with "W" access to non-wildcard repos to auto-viv repos) - if you're treating this the same as a wildcard creation, you end up making this guy the "creater" of that repo, which means he can add users etc... We resolve that paranois by disallowing autoviv of "W" access repos at all... Only "C" access repos can be autovived by a user (this will be in the next commit) --- src/gitolite.pm | 8 +------- src/gl-compile-conf | 6 +----- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 23f7e23..657c0fa 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -179,15 +179,9 @@ sub parse_acl return unless $repo; return $ENV{GL_REPOPATT} = "" if $repos{$repo}; - - # didn't find $repo in %repos, so it must be a wildcard-match case - - # note that the repo regexes in %repos have a leading ^ but not a trailing - # $; we need to add the $ here to complete the "line-anchoring" - my @matched = grep { $repo =~ /$_$/ } sort keys %repos; + my @matched = grep { $repo =~ /^$_$/ } sort keys %repos; die "$repo has no matches\n" unless @matched; die "$repo has multiple matches\n@matched\n" if @matched > 1; - # found exactly one pattern that matched, copy its ACL $repos{$repo} = $repos{$matched[0]}; # and return the pattern diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 9da28ed..7d8e1d9 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -195,11 +195,7 @@ sub parse_conf_file @repos = split ' ', $1; @repos = expand_list ( @repos ); - # CREAT[EO]R must be changed to $creater. Also, prefix a "^" to - # force it to look like a regex. Otherwise, foo/CREATER/bar (no - # regex metas) looks like an ordinary reponame, and the logic (in - # gl-auth) that decides when to allow autovivify gets confused. - s/\bCREAT[EO]R\b/\$creater/g && s/^/^/ for @repos; + s/\bCREAT[EO]R\b/\$creater/g for @repos; } # actual permission line elsif (/^(-|C|R|RW|RW\+) (.* )?= (.+)/) From 6fb2296e2c7b136d25d86fc487aafce101a7201a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 21 Dec 2009 17:49:21 +0530 Subject: [PATCH 194/850] autoviv new repos by user only on "C" access we've removed the facility of auto-viving "W" access repos when they are not wildcards. A wildcard pattern like foo/CREATER was indistinguishable from a non-wildcard repo, and resolving it was becoming kludgier and kludgier. (See the revert in the commit before this one for details). As a side effect of not being able to distinguish wildcard repos from real repos easily, the expand command now works for a normal repo too (because we have to make it work for "foo/CREATER") --- src/gitolite.pm | 2 ++ src/gl-auth-command | 18 ++++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 657c0fa..1a3655f 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -179,6 +179,8 @@ sub parse_acl return unless $repo; return $ENV{GL_REPOPATT} = "" if $repos{$repo}; + + # didn't find $repo in %repos, so it must be a wildcard-match case my @matched = grep { $repo =~ /^$_$/ } sort keys %repos; die "$repo has no matches\n" unless @matched; die "$repo has multiple matches\n@matched\n" if @matched > 1; diff --git a/src/gl-auth-command b/src/gl-auth-command index c5b78ae..1ced74c 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -99,7 +99,7 @@ if ($cmd =~ $CUSTOM_COMMANDS) { # with an actual reponame, you can "getperms" or "setperms" get_set_perms($repo_base_abs, $repo, $verb, $user); } - elsif ($repo !~ $REPONAME_PATT and $verb eq 'expand') { + elsif ($verb eq 'expand') { # with a wildcard, you can "expand" it to see what repos actually match expand_wild($GL_CONF_COMPILED, $repo_base_abs, $repo, $user); } else { @@ -143,20 +143,14 @@ die "$repo has two consecutive periods; I don't like that\n" if $repo =~ /\.\./; if ( -d "$repo_base_abs/$repo.git" ) { # existing repo my ($creater, $user_R, $user_W) = &repo_rights($repo_base_abs, $repo, $user); - my $patt = &parse_acl($GL_CONF_COMPILED, $repo, $creater, $user_R, $user_W); + &parse_acl($GL_CONF_COMPILED, $repo, $creater, $user_R, $user_W); } else { - my $patt = &parse_acl($GL_CONF_COMPILED, $repo, $user, $user, $user); - # parse_acl returns "" if the repo was non-wildcard, or the pattern - # that matched if it was a wildcard + &parse_acl($GL_CONF_COMPILED, $repo, $user, $user, $user); - # auto-vivify new repo; 2 situations allow autoviv -- normal repos - # with W access (the old mode), and wildcard repos with C access - my $W_ok = $repos{$repo}{W}{$user} || $repos{$repo}{W}{'@all'}; - my $C_ok = $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'}; - if ($W_ok and not $patt or $C_ok and $patt) { + # auto-vivify new repo if you have C access + if ( $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'} ) { wrap_chdir("$repo_base_abs"); - # for wildcard repos, we also want to set the "creater" - new_repo($repo, "$GL_ADMINDIR/src/hooks", ( $patt ? $user : "")); + new_repo($repo, "$GL_ADMINDIR/src/hooks", $user); wrap_chdir($ENV{HOME}); } } From 03babe77727b4290d4e3aa6199c692e0ce3ca6fb Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 21 Dec 2009 17:07:31 +0530 Subject: [PATCH 195/850] parse_acl (gitolite.pm): minor convenience added --- src/gitolite.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 1a3655f..4cfa8e1 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -175,7 +175,8 @@ sub parse_acl die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; - # access reporting doesn't send $repo, and doesn't need to + # basic access reporting doesn't send $repo, and doesn't need to; you just + # want the config dumped as is, really return unless $repo; return $ENV{GL_REPOPATT} = "" if $repos{$repo}; @@ -222,6 +223,10 @@ sub expand_wild { my($GL_CONF_COMPILED, $repo_base_abs, $repo, $user) = @_; + # this is for convenience; he can copy-paste the output of the basic + # access report instead of having to manually change CREATER to his name + $repo =~ s/\bCREAT[EO]R\b/$user/g; + # display matching repos (from *all* the repos in the system) that $user # has at least "R" access to From f37fb451447639b6ceeb1f389f371068ba5b0b90 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Mon, 21 Dec 2009 01:55:45 +0200 Subject: [PATCH 196/850] compile: support "repo @all" definitions "repo @all" can be used to set permissions or configurations for all already defined repos. (A repository is defined if it has permission rules associated, empty "repo" stanza or "@group=..." line is not enough.) For example to allow a backup user to clone all repos: # All other configuration [...] repo @all R = backup Signed-off-by: Teemu Matilainen --- conf/example.conf | 7 +++++++ src/gl-compile-conf | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/conf/example.conf b/conf/example.conf index 84ccff2..6204c91 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -101,6 +101,13 @@ repo gitolite repo @oss_repos R = @all + # set permissions to all already defined repos + # (a repository is defined if it has permission rules + # associated, empty "repo" stanza or "@group=..." line is + # not enough) +repo @all + RW+ = @admins + # ADVANCED PERMISSIONS USING REFEXES # - refexes are specified in perl regex syntax diff --git a/src/gl-compile-conf b/src/gl-compile-conf index b8e8a1c..4c34d1d 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -185,7 +185,11 @@ sub parse_conf_file { # grab the list and expand any @stuff in it @repos = split ' ', $1; - @repos = expand_list ( @repos ); + if (@repos == 1 and $repos[0] eq '@all') { + @repos = keys %repos; + } else { + @repos = expand_list ( @repos ); + } } # actual permission line elsif (/^(-|R|RW|RW\+) (.* )?= (.+)/) From ba3cbd7ecf02078b84e1527cc27e923306358727 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 21 Dec 2009 22:58:47 +0530 Subject: [PATCH 197/850] doc/3, conf: document @all for repos plus some refactoring of doc/3 --- conf/example.conf | 3 +- doc/3-faq-tips-etc.mkd | 72 ++++++++++++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 6204c91..26317e2 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -104,7 +104,8 @@ repo @oss_repos # set permissions to all already defined repos # (a repository is defined if it has permission rules # associated, empty "repo" stanza or "@group=..." line is - # not enough) + # not enough). *Please* do see doc/3-faq-tips-etc.mkd for + # some important notes on this feature repo @all RW+ = @admins diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 0e3af99..a7ee05a 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -5,6 +5,10 @@ In this document: * common errors and mistakes * git version dependency * other errors, warnings, notes... + * ssh-copy-id + * cloning an empty repo + * `@all` syntax for repos + * umask setting * getting a tar file from a clone * differences from gitosis * simpler syntax @@ -76,38 +80,58 @@ normal way, since it's not empty anymore. ### other errors, warnings, notes... - * don't have `ssh-copy-id`? This is broadly what that command does, if you - want to replicate it manually. The input is your pubkey, typically - `~/.ssh/id_rsa.pub` from your client/workstation. +#### ssh-copy-id - * it copies it to the server as some file +don't have `ssh-copy-id`? This is broadly what that command does, if you want +to replicate it manually. The input is your pubkey, typically +`~/.ssh/id_rsa.pub` from your client/workstation. - * it appends that file to `~/.ssh/authorized_keys` on the server - (creating it if it doesn't already exist) + * it copies it to the server as some file - * it then makes sure that all these files/directories have go-w perms - set (assuming user is "git"): + * it appends that file to `~/.ssh/authorized_keys` on the server + (creating it if it doesn't already exist) - /home/git/.ssh/authorized_keys - /home/git/.ssh - /home/git + * it then makes sure that all these files/directories have go-w perms + set (assuming user is "git"): - [Actually, sshd requires that even directories *above* ~ (/, /home, - typically) also must be `go-w`, but that needs root. And typically - they're already set that way anyway. (Or if they're not, you've got - bigger problems than gitolite install not working!)] + /home/git/.ssh/authorized_keys + /home/git/.ssh + /home/git - * cloning an empty repo is only possible with clients greater than 1.6.2. - So at least one of your clients needs to have a recent git. Once at least - one commit has been made, older clients can also use it +[Actually, sshd requires that even directories *above* ~ (/, /home, +typically) also must be `go-w`, but that needs root. And typically +they're already set that way anyway. (Or if they're not, you've got +bigger problems than gitolite install not working!)] - * when you clone an empty repo, git seems to complain about `fatal: The - remote end hung up unexpectedly`. However, you can ignore this, since it - doesn't seem to hurt anything. [Update 2009-09-14; this has been fixed in - git 1.6.4.3] +#### cloning an empty repo - * gitweb not able to read your repos? You can change the umask for newly - created repos to something more relaxed -- see the `~/.gitolite.rc` file +Cloning an empty repo is only possible with clients greater than 1.6.2. So at +least one of your clients needs to have a recent git. Once at least one +commit has been made, older clients can also use it + +When you clone an empty repo, git seems to complain about `fatal: The remote +end hung up unexpectedly`. However, you can ignore this, since it doesn't +seem to hurt anything. [Update 2009-09-14; this has been fixed in git +1.6.4.3] + +#### `@all` syntax for repos + +There *is* a way to use the `@all` syntax for repos also, as described in +`conf/example.conf`. However, there is an important difference between this +and the old `@all` (for users): + + * `@all` for repos is immediately expanded, when found, into the currently + known list of repos. "Currently" means upto this point in the config + file, and "known" means having some user with some permissions associated + with the repo! + + * This means that if you really want *all* repos, you'd better put this para + at the **end** of the config file! + +#### umask setting + +Gitweb not able to read your repos? You can change the umask for newly +created repos to something more relaxed -- see the `~/.gitolite.rc` file ### getting a tar file from a clone From 9c3abb20e1138a53d6d42521ffe6d7aec44b98aa Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 22 Dec 2009 14:27:40 +0530 Subject: [PATCH 198/850] easy install: minor user message change for first-time install --- src/gl-easy-install | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index b74621a..819eb20 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -199,8 +199,8 @@ version_info() { git describe --tags --long HEAD 2>/dev/null > src/VERSION || echo '(unknown)' > src/VERSION # what was the old version there? - export upgrade_details="you are upgrading from \ - $(ssh -p $port $user@$host cat gitolite-install/src/VERSION 2>/dev/null || echo '(unknown)' ) \ + export upgrade_details="you are upgrading \ + $(ssh -p $port $user@$host cat gitolite-install/src/VERSION 2>/dev/null || echo '(or installing first-time)' ) \ to $(cat src/VERSION)" prompt "$upgrade_details" "$v_upgrade_details" From b0ce84d47f02d2f6cf920ef19b61aa225667bf9a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 23 Dec 2009 19:56:53 +0530 Subject: [PATCH 199/850] document @SHELL feature, allow "info" for all, ...but still distinguish shell folks with a small extra line telling them they have shell access --- conf/example.conf | 16 ++++++++++++++++ doc/6-ssh-troubleshooting.mkd | 15 +++++++++++++++ src/gl-auth-command | 3 ++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/conf/example.conf b/conf/example.conf index 26317e2..2c3c6fb 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -212,3 +212,19 @@ repo gitolite # This does either a plain "git config section.key value" (for the first 3 # examples above) or "git config --unset-all section.key" (for the last # example). Other forms (--add, the value_regex, etc) are not supported. + +# SHELL ACCESS +# ------------ + +# It is possible to give certain users shell access as well as allow them to +# use gitolite features for their git repo access. The idea is to eliminate +# the need for 2 keys when both shell and gitolite access are needed. + +# To give a user shell access, add the username to the special @SHELL group: + +@SHELL = sitaram + +# Do not add people to this group indiscriminately. AUDITABILITY OF ACCESS +# CONTROL CHANGES (AND OF REPO ACCESSES) WILL BE COMPROMISED IF ADMINS CAN +# FIDDLE WITH THE ACTUAL (PLAIN TEXT) LOG FILES THAT GITOLITE KEEPS, WHICH +# THEY CAN EASILY DO IF THEY HAVE A SHELL. diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 4bf980f..9602dd2 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -1,5 +1,20 @@ # ssh troubleshooting +Update 2009-12-23: most of this document is now of historical interest and +will be totally revamped when I have time. For now, just note this amendment. + +The document below says "we can't use the same key for both [gitolite access +and shell access]...". We've managed (thanks to an idea from Jesse Keating) +to get around this. Now it *is* possible for a single key to allow both +gitolite access *and* shell access. + +This is done by placing such a user in a special `@SHELL` group in the +gitolite config file. As usual, please see `conf/example.conf` for more info +on this, since I'm using that as a central place to document anything +concerned with the conf file. + +---- + Ssh has always been the biggest troublespot in all this. While gitolite makes it as easy as possible, you might still run into trouble sometimes. diff --git a/src/gl-auth-command b/src/gl-auth-command index 2de7d43..1e0d1d2 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -82,8 +82,9 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; # people allowed to get a shell can get basic access info by asking nicely -if ($shell_allowed and $cmd eq 'info') { +if ($cmd eq 'info') { &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); + print "you also have shell access\n\r" if $shell_allowed; exit 1; } From d03152316f0d0d96c2a6c11c5ee6c7b31b0dc7d8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 24 Dec 2009 22:35:43 +0530 Subject: [PATCH 200/850] install transcript --- doc/7-install-transcript.mkd | 211 +++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 doc/7-install-transcript.mkd diff --git a/doc/7-install-transcript.mkd b/doc/7-install-transcript.mkd new file mode 100644 index 0000000..026c98e --- /dev/null +++ b/doc/7-install-transcript.mkd @@ -0,0 +1,211 @@ +# gitolite install transcript + +This is a *complete* transcript of a full gitolite install, *from scratch*, +using brand new userids ("sita" on the client, "git" on the server). Please +note that you can use existing userids also, it is not necessary to use +dedicated user IDs for this. Also, you don't have to use some *other* server +for all this, both server and client can be "localhost" if you like. + +Please note that this entire transcript can be summarised as: + + * create users on client and server (optional) + * get pubkey access to server from client (`ssh-copy-id` or manual eqvt) + * run one command ***on client*** (`gl-easy-install`) + +...and only that last step is actually gitolite. In fact, the bulk of the +transcript is **non**-gitolite stuff :) + +---- + +### create userids on server and client (optional) + +Client side: add user, give him a password + + sita-lt:~ # useradd sita + + sita-lt:~ # passwd sita + Changing password for user sita. + New UNIX password: + Retype new UNIX password: + passwd: all authentication tokens updated successfully. + +Server side: (log on to server, then) add user, give it a password + + sita-lt:~ # ssh sitaram@server + sitaram@server's password: + Last login: Fri Dec 18 20:25:06 2009 + -bash-3.2$ su - + Password: + + sita-sv:~ # useradd git + + sita-sv:~ # passwd git + Changing password for user git. + New UNIX password: + Retype new UNIX password: + passwd: all authentication tokens updated successfully. + +Server side: allow ssh access to "git" user + +This is done by editing the sshd config file and adding "git" to the +"AllowUsers" list (the grep command is just confirming the change we made, +because I'm not showing the actual "vi" session): + + sita-sv:~ # vim /etc/ssh/sshd_config + + sita-sv:~ # grep -i allowusers /etc/ssh/sshd_config + AllowUsers sitaram git + + sita-sv:~ # service sshd restart + Stopping sshd: [ OK ] + Starting sshd: [ OK ] + +---- + +### get pubkey access from client to server + +This involves creating a keypair for yourself (using `ssh-keygen`), and +copying the public part of that keypair to the `~/.ssh/authorized_keys` file +on the server (using `ssh-copy-id`, if you're on Linux, or the manual method +described in the `ssh-copy-id` section in `doc/3-faq-tips-etc.mkd`). + + sita-lt:~ $ su - sita + Password: + + sita@sita-lt:~ $ ssh-keygen + Generating public/private rsa key pair. + Enter file in which to save the key (/home/sita/.ssh/id_rsa): + Created directory '/home/sita/.ssh'. + Enter passphrase (empty for no passphrase): + Enter same passphrase again: + Your identification has been saved in /home/sita/.ssh/id_rsa. + Your public key has been saved in /home/sita/.ssh/id_rsa.pub. + The key fingerprint is: + 8a:e0:60:1b:04:58:68:50:a4:d7:d0:3a:a5:2d:bf:0a sita@sita-lt.atc.tcs.com + The key's randomart image is: + +--[ RSA 2048]----+ + |===. | + |+o oo | + |o..=. | + |..= . | + |.o.+ S | + |.oo... . | + |E.. ... | + | . . | + | .. | + +-----------------+ + + sita@sita-lt:~ $ ssh-copy-id -i ~/.ssh/id_rsa git@server + git@server's password: + /usr/bin/xauth: creating new authority file /home/git/.Xauthority + Now try logging into the machine, with "ssh 'git@server'", and check in: + + .ssh/authorized_keys + + to make sure we haven't added extra keys that you weren't expecting. + +Double check to make sure you can log on to `git@server` without a password: + + sita@sita-lt:~ $ ssh git@server pwd + /home/git + +---- + +### get gitolite source + + sita@sita-lt:~ $ git clone git://github.com/sitaramc/gitolite gitolite-source + Initialized empty Git repository in /home/sita/gitolite-source/.git/ + remote: Counting objects: 1157, done. + remote: Compressing objects: 100% (584/584), done. + remote: Total 1157 (delta 756), reused 912 (delta 562) + Receiving objects: 100% (1157/1157), 270.08 KiB | 61 KiB/s, done. + Resolving deltas: 100% (756/756), done. + +### install gitolite + +Note that gitolite is installed from the *client*. The `easy-install` script +runs on the client but installs gitolite on the server! + + sita@sita-lt:~ $ cd gitolite-source/src + + **This is the only gitolite specific command in a typical +install sequence**. Run it without any arguments to see a usage +message. Run it without the `-q` to get a more verbose, pause-at-every-step, +install mode that allows you to change the defaults etc. + + + sita@sita-lt:src $ ./gl-easy-install -q git server sitaram + you are upgrading (or installing first-time) to v0.95-38-gb0ce84d + setting up keypair... + Generating public/private rsa key pair. + Enter passphrase (empty for no passphrase): + Enter same passphrase again: + Your identification has been saved in /home/sita/.ssh/sitaram. + Your public key has been saved in /home/sita/.ssh/sitaram.pub. + The key fingerprint is: + 2a:8e:88:42:36:7e:71:e8:cc:ff:4c:54:64:8e:cf:19 sita@sita-lt.atc.tcs.com + The key's randomart image is: + +--[ RSA 2048]----+ + | o | + | = | + | . E | + | + o | + | . .S+ | + | + o ... | + |+ = + .. | + |oo B .o | + |+ o o..o | + +-----------------+ + creating gitolite para in ~/.ssh/config... + finding/creating gitolite rc... + installing/upgrading... + Initialized empty Git repository in /home/git/repositories/gitolite-admin.git/ + Initialized empty Git repository in /home/git/repositories/testing.git/ + Pseudo-terminal will not be allocated because stdin is not a terminal. + fatal: No HEAD commit to compare with (yet) + [master (root-commit) 2f40d4b] start + 2 files changed, 13 insertions(+), 0 deletions(-) + create mode 100644 conf/gitolite.conf + create mode 100644 keydir/sitaram.pub + cloning gitolite-admin repo... + Initialized empty Git repository in /home/sita/gitolite-admin/.git/ + remote: Counting objects: 6, done. + remote: Compressing objects: 100% (4/4), done. + remote: Total 6 (delta 0), reused 0 (delta 0) + Receiving objects: 100% (6/6), done. + + + --------------------------------------------------------------- + + done! + + Reminder: + *Your* URL for cloning any repo on this server will be + gitolite:reponame.git + *Other* users you set up will have to use + git@server:reponame.git + + If this is your first time installing gitolite, please also: + tail -31 ./gl-easy-install + for next steps. + +---- + +### examine what you have + + sita@sita-lt:src $ cd ~/gitolite-admin/ + + sita@sita-lt:gitolite-admin $ git --no-pager log --stat + commit 2f40d4bb80d424dc39aae5d0973f8c1b2e395666 + Author: git + Date: Thu Dec 24 21:39:15 2009 +0530 + + start + + conf/gitolite.conf | 12 ++++++++++++ + keydir/sitaram.pub | 1 + + 2 files changed, 13 insertions(+), 0 deletions(-) + +And that's really all. Add keys to keydir here, edit conf/gitolite.conf as +needed, then add, commit, and push the changes to the server. Try out that +`tail -31 ./gl-easy-install` too :) From 79647078a3d285a7d0d6a97741f3b3bf3aa3fde0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 25 Dec 2009 01:13:31 +0530 Subject: [PATCH 201/850] auth: regex goof on my part for those not yet able to upgrade (or until I merge this into the branch you care about), if you have a repo called, say "bk2git", just refer to it as "bk2git.git" in the clone command! [Thanks to Mark Frazer for finding this...] --- src/gl-auth-command | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 1e0d1d2..d6482ed 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -96,7 +96,7 @@ if ($cmd eq 'info') { # git-receive-pack 'reponame.git' # including the single quotes -my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:.git)?'/); +my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/); unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) { # if the user is allowed a shell, just run the command exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND} if $shell_allowed; From ab3c861241b774b73e5e3b6d7abaf642f0231939 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 30 Dec 2009 22:06:16 +0530 Subject: [PATCH 202/850] Revert "easy install: needs a minor fix to accommodate auto-vivification" This reverts commit 6576e82e3342a869e18845df432ef4128e693131. On oddball configs, where the shell key is reused as the gitolite key by smart( people|-alecks), the ls-remote stops the program dead, preventing the "git add" and "git commit" that seed the admin repo. This makes extra work in terms of fixing it after the fact; removing it makes the install go further, and all you need to do is (1) delete the first line from ~/.ssh/authorized_keys on the server and (2) back on the client do a "git clone gitolite:gitolite-admin". OK so it needs to be removed. Explaining that was the easy part! The hard part is explaining why removing it is harmless. Look at the commit tree around that commit, and see that the commit before that (b78a720) was partially reverted in e7e6085. b78a720 removed the new_repo call from compile, forcing it to happen only on auth, which forced this workaround for seeding the admin repo. Since e7e6085 reverted that part of b78a720, giving back new_repo functions to compile, this line of code wasn't doing any good. QED and all that :) --- src/gl-easy-install | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 819eb20..6077e23 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -401,7 +401,6 @@ setup_pta() { # Substitute $GL_ADMINDIR and $REPO_BASE appropriately. Note there is no # space around the "=" in the second and third lines. - git ls-remote gitolite:gitolite-admin echo "cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start From 5ad2056a9c1e25f7da35ce69ab4bed7c47dbf56a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 8 Jan 2010 06:20:19 +0530 Subject: [PATCH 203/850] typo fix in doc/4; thanks Teemu! --- doc/4-wildcard-repositories.mkd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 1feac40..2324a02 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -143,7 +143,7 @@ Create a small text file that contains the permissions you desire: RW u6 (hit ctrl-d here) -...and use the new "getperms" command to set permissions for your repo: +...and use the new "setperms" command to set permissions for your repo: $ ssh git@server setperms assignments/u4/a12 < myperms New perms are: From 7124faa9f3ee06bb095b8eea3cea1caeab3a885f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 7 Jan 2010 17:59:34 +0530 Subject: [PATCH 204/850] NAME-based restrictions Gitolite allows you to restrict changes by file/dir name. The syntax for this used "PATH/" as a prefix to denote such file/dir patterns. This has now been changed to "NAME/" because PATH is potentially confusing. While this is technically a backward-incompatible change, the feature itself was hitherto undocumented, and only a few people were using it, so I guess it's not that bad... Also added documentation now. --- conf/example.conf | 34 ++++++++++++++++++++++++++++++++++ doc/3-faq-tips-etc.mkd | 10 ++++++++++ src/gl-compile-conf | 10 +++++----- src/hooks/update | 10 +++++----- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 2c3c6fb..a9b037c 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -165,6 +165,40 @@ repo git # looking for (`W` or `+`), or a minus (`-`), results in success, or failure, # respectively. A fallthrough also results in failure +# FILE/DIR NAME BASED RESTRICTIONS +# -------------------------------- + +# Here's a hopefully self-explanatory example. Assume the project has the +# following contents at the top level: a README, a "doc/" directory, and an +# "src/" directory. + +repo foo + RW+ = lead_dev # rule 1 + RW = dev1 dev2 dev3 dev4 # rule 2 + + RW NAME/ = lead_dev # rule 3 + RW NAME/doc/ = dev1 dev2 # rule 4 + RW NAME/src/ = dev1 dev2 dev3 dev4 # rule 5 + +# Notes + +# - the "NAME/" is part of the syntax; think of it as a keyword if you like + +# - file/dir NAME-based restrictions are *in addition* to normal (branch-name +# based) restrictions; they are not a *replacement* for them. This is why +# rule #2 (or something like it, maybe with a more specific branch-name) is +# needed; without it, dev1/2/3/4 cannot push any branches. + +# - if a repo has *any* NAME/ rules, then NAME-based restrictions are checked +# for *all* users. This is why rule 3 is needed, even though we don't +# actually have any NAME-based restrictions on lead_dev. Notice the pattern +# on rule 3. + +# - *each* file touched by the commits being pushed is checked against those +# rules. So, lead_dev can push changes to any files, dev1/2 can push +# changes to files in "doc/" and "src/" (but not the top level README), and +# dev3/4 can only push changes to files in "src/". + # GITWEB AND DAEMON STUFF # ----------------------- diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index a7ee05a..808cf03 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -13,6 +13,7 @@ In this document: * differences from gitosis * simpler syntax * two levels of access rights checking + * file/dir NAME based restrictions * error checking the config file * delegating parts of the config file * easier to specify gitweb "description" and gitweb/daemon access @@ -235,6 +236,15 @@ any of the refexes match, the push succeeds. If none of them match, it fails. Gitolite also allows "exclude" or "deny" rules. See later in this document for details. +#### file/dir NAME based restrictions + +In addition to branch-name based restrictions, gitolite also allows you to +restrict what files or directories can be involved in changes being pushed. +This basically uses `git diff --name-only` to obtain the list of files being +changed, treating each filename as a "ref" to be matched. + +Please see `conf/example.conf` for syntax and examples. + #### error checking the config file gitosis does not do any. I just found out that if you mis-spell `members` as diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 4c34d1d..23f26c7 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -200,9 +200,9 @@ sub parse_conf_file # if no ref is given, this PERM applies to all refs @refs = qw(refs/.*) unless @refs; - # fully qualify refs that dont start with "refs/" or "PATH/"; + # fully qualify refs that dont start with "refs/" or "NAME/"; # prefix them with "refs/heads/" - @refs = map { m(^(refs|PATH)/) or s(^)(refs/heads/); $_ } @refs; + @refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs; # expand the user list, unless it is just "@all" @users = expand_list ( @users ) @@ -239,13 +239,13 @@ sub parse_conf_file # for 2nd level check, store each "ref, perms" pair in order for my $ref (@refs) { - # checking PATH based restrictions is expensive for + # checking NAME based restrictions is expensive for # the update hook (see the changes to src/hooks/update # in this commit for why) so we would *very* much like # to avoid doing it for the large majority of repos - # that do *not* use PATH limits. Setting a flag that + # that do *not* use NAME limits. Setting a flag that # can be checked right away will help us do that - $repos{$repo}{PATH_LIMITS} = 1 if $ref =~ /^PATH\//; + $repos{$repo}{NAME_LIMITS} = 1 if $ref =~ /^NAME\//; push @{ $repos{$repo}{$user} }, { $ref => $perms } unless $rurp_seen{$repo}{$user}{$ref}{$perms}++; } diff --git a/src/hooks/update b/src/hooks/update index 235fa40..a3c1be4 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -65,12 +65,12 @@ push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; # prepare the list of refs to be checked # previously, we just checked $ref -- the ref being updated, which is passed -# to us by git (see man githooks). Now we also have to treat each PATH being -# updated as a potential "ref" and check that, if PATH-based restrictions have +# to us by git (see man githooks). Now we also have to treat each NAME being +# updated as a potential "ref" and check that, if NAME-based restrictions have # been specified my @refs = ($ref); # the first ref to check is the real one -if (exists $repos{$ENV{GL_REPO}}{PATH_LIMITS}) { +if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) { # this is special to git -- the hash of an empty tree my $empty='4b825dc642cb6eb9a060e54bf8d69288fbee4904'; # well they're not really "trees" but $empty is indeed the empty tree so @@ -78,7 +78,7 @@ if (exists $repos{$ENV{GL_REPO}}{PATH_LIMITS}) { # diff' only wants trees my $oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha; my $newtree = $newsha eq '0' x 40 ? $empty : $newsha; - push @refs, map { chomp; s/^/PATH\//; $_; } `git diff --name-only $oldtree $newtree`; + push @refs, map { chomp; s/^/NAME\//; $_; } `git diff --name-only $oldtree $newtree`; } my $refex = ''; @@ -111,7 +111,7 @@ sub check_ref { # and in this version, we have many "refs" to check. The one we print in the # log is the *first* one (which is a *real* ref, like refs/heads/master), -# while all the rest (if they exist) are like PATH/something. So we do the +# while all the rest (if they exist) are like NAME/something. So we do the # first one separately to capture it, then run the rest (if any) my $log_refex = check_ref(shift @refs); check_ref($_) for @refs; From 08ef3555a1ca3797c9873f41ca293b23d7f6d12f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 9 Jan 2010 19:57:44 +0530 Subject: [PATCH 205/850] deprecation warning about old style PATH/ syntax (this commit will probably get reverted after a suitable period has elapsed and no one is likely to still be using the old syntax). Forgetting to change it to NAME/ after is a security issue -- you end up permitting stuff you don't want to! This commit allows the old syntax but prints a warning --- src/gl-compile-conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 23f26c7..2787893 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -200,6 +200,8 @@ sub parse_conf_file # if no ref is given, this PERM applies to all refs @refs = qw(refs/.*) unless @refs; + # deprecation warning + map { warn "WARNING: old syntax 'PATH/' found; please use new syntax 'NAME/'\n" if s(^PATH/)(NAME/) } @refs; # fully qualify refs that dont start with "refs/" or "NAME/"; # prefix them with "refs/heads/" @refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs; From 839027f7a798832a6ddc4d815672622ef085be61 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 7 Jan 2010 06:56:04 +0530 Subject: [PATCH 206/850] change delegation to NAME/ style (warning: backward compat breakage) This is a backward incompatible change. If you are using delegation and you upgrade to this version, please do the following: * change your gitolite.conf file to use the new syntax (see doc/5-delegation.mkd in this commit) * for each branch "foo" in the gitolite-admin repo, do this: # (on "master" branch) git checkout foo -- conf/fragments/foo.conf * git add all those new fragments and commit to master * delete all the branches on your clone and the server # again, for each branch foo git branch -D foo git push origin :foo --- doc/5-delegation.mkd | 34 +++++++++++++++++----------------- src/ga-post-update-hook | 23 ----------------------- 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index af73b81..6c62b24 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -61,35 +61,35 @@ You do this by adding branches to the `gitolite-admin` repo: # the admin repo access was probably like this to start with: repo gitolite-admin - RW+ = sitaram + RW+ = sitaram # now add these lines to the config for the admin repo - RW webbrowser_repos = alice - RW webserver_repos = bob - RW malware_repos = mallory + RW NAME/conf/fragments/webbrowser_repos = alice + RW NAME/conf/fragments/webserver_repos = bob + RW NAME/conf/fragments/malware_repos = mallory + +This uses gitolite's ability to restrict pushes by file/dir name being changed +-- the syntax you see above ensures that, while "sitaram" does not have any +NAME based restrictions, the other 3 users do. See `conf/example.conf` for +syntax and notes. As you can see, **for each repo group** you want to delegate authority over, -there's a **branch with the same name** in the `gitolite-admin` repo. If you -have write access to that branch, you are allowed to define rules for repos in -that repo group. +there's a rule for a **corresponding file** in `conf/fragments` in the +`gitolite-admin` repo. If you have write access to that file, you are allowed +to define rules for repos in that repo group. -In other words, we use gitolite's per-branch permissions to "enforce" the -separation between the delegated configs! +In other words, we use gitolite's file/dir NAME-based permissions to "enforce" +the separation between the delegated configs! Here's how to use this in practice: - * Alice clones the `gitolite-admin` repo, creates (if not already created) and - checks out a new branch called `webbrowser_repos`, and adds a file called - `conf/fragments/webbrowser_repos.conf` in that branch - - * (the rest of the contents of that branch do not matter; she can keep - all the other files or delete all of them -- it doesn't make any - difference. Only that one specific file is used). + * Alice clones the `gitolite-admin` repo, and adds a file called + `conf/fragments/webbrowser_repos.conf` * she writes in this file any access control rules for the "firefox" and "lynx" repos. She should not write access rules for any other project -- they will be ignored - * Alice then commits and pushes this branch to the `gitolite-admin` repo + * Alice then commits and pushes to the `gitolite-admin` repo Naturally, a successful push invokes the post-update hook that the admin repo has, which eventually runs the compile script. The **net effect** is as if diff --git a/src/ga-post-update-hook b/src/ga-post-update-hook index 91d2bfb..b84dfa8 100755 --- a/src/ga-post-update-hook +++ b/src/ga-post-update-hook @@ -4,28 +4,5 @@ # (the GL_ADMINDIR env var would have been set by gl-auth-command) GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master -# remove all fragments. otherwise, you get spurious error messages when you -# take away someone's delegation in the main config but the fragment is still -# hanging around. The ones that are valid will get re-created anyway -rm -rf $GL_ADMINDIR/conf/fragments -# collect all the delegated fragments -mkdir $GL_ADMINDIR/conf/fragments -for br in `git for-each-ref --format='%(refname:short)'` -do - # skip master (duh!) - [ "$br" = "master" ] && continue - - # all other branches *should* contain a file called .conf - # inside conf/fragments; if so copy it - if git show $br:conf/fragments/$br.conf > /dev/null 2>&1 - then - git show $br:conf/fragments/$br.conf > $GL_ADMINDIR/conf/fragments/$br.conf - echo "(extracted $br conf; `wc -l < $GL_ADMINDIR/conf/fragments/$br.conf` lines)" - else - echo " ***** ERROR *****" - echo " branch $br does not contain conf/fragments/$br.conf" - fi -done - cd $GL_ADMINDIR $GL_BINDIR/gl-compile-conf From 6c38e30e9ae2ec206913e6e0eca5dc29abee7a27 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Tue, 5 Jan 2010 20:34:40 +0200 Subject: [PATCH 207/850] compile: support "include" definition Support config file including using: include "filename" If filename is not an absolute path, it is looked from the $GL_ADMINDIR/conf/ directory. For security reasons include is not allowed for fragments. Signed-off-by: Teemu Matilainen --- src/gl-compile-conf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 2787893..b654e69 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -264,6 +264,16 @@ sub parse_conf_file $repo_config{$repo}{$key} = $value; } } + # include + elsif (/^include "(.+)"/) + { + my $file = $1; + $file = "$GL_ADMINDIR/conf/$file" unless $file =~ /^\//; + die "$WARN $fragment attempting to include configuration\n" if $fragment ne 'master'; + die "$ABRT included file not found: '$file'\n" unless -f $file; + + parse_conf_file($file, $fragment); + } # very simple syntax for the gitweb description of repo; one of: # reponame = "some description string" # reponame "owner name" = "some description string" From 15475f666c07e66d91fd00added2a50544d9221b Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Fri, 8 Jan 2010 14:05:11 +0200 Subject: [PATCH 208/850] Fix exit codes for allowed ssh commands gitolite specific ssh commands ("getperms", "setperms", "info" etc.) should exit with non-error code in case of success. Also "get/setperms" should print to STDOUT instead of STDERR. This change is specially needed for the gitolite-tools (http://github.com/tmatilai/gitolite-tools) to work. Signed-off-by: Teemu Matilainen --- src/gitolite.pm | 6 +++--- src/gl-auth-command | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 4cfa8e1..74e1bbc 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -137,11 +137,11 @@ sub get_set_perms wrap_chdir("$repo_base_abs"); wrap_chdir("$repo.git"); if ($verb eq 'getperms') { - print STDERR `cat gl-perms 2>/dev/null`; + system("cat", "gl-perms") if -f "gl-perms"; } else { system("cat > gl-perms"); - print STDERR "New perms are:\n"; - print STDERR `cat gl-perms`; + print "New perms are:\n"; + system("cat", "gl-perms"); } } diff --git a/src/gl-auth-command b/src/gl-auth-command index 6be5bde..1d6a33a 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -105,14 +105,14 @@ if ($cmd =~ $CUSTOM_COMMANDS) { } else { die "$cmd doesn't make sense to me\n"; } - exit 1; + exit 0; } # people allowed to get a shell can get basic access info by asking nicely if ($cmd eq 'info') { &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); print "you also have shell access\n\r" if $shell_allowed; - exit 1; + exit 0; } # ---------------------------------------------------------------------------- From a9824464e5729f1c8b031b491c384a49a199d4ff Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 13 Jan 2010 14:59:45 +0530 Subject: [PATCH 209/850] update hook: anchor refex with ^ when matching refs Currently, a line like RW foo = user1 allows user1 to push any ref that contains the string refs/heads/foo. This includes refs like refs/heads/foo refs/heads/foobar refs/heads/foo/bar which is fine; that is what is intended. (You can always use foo$ instead of foo if you want to prevent the latter two). Similarly, RW refs/foo = user1 allows refs/foo refs/foobar refs/foo/bar Now, I don't see this as a "security risk" but the fact is that this allows someone to clutter your repo with junk like refs/bar/refs/heads/foo refs/heads/bar/refs/heads/foo (or, with the second config line example, refs/bar/refs/foo refs/heads/bar/refs/foo ) My personal advice is if you find someone doing that intentionally, you should probably take him out and shoot him [*], but since now *two* people have complained about this, here goes... ---- [*] you don't have to take him out if you don't want to --- src/hooks/update | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/update b/src/hooks/update index a3c1be4..c1ff18f 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -100,7 +100,7 @@ sub check_ref { for my $ar (@allowed_refs) { $refex = (keys %$ar)[0]; # refex? sure -- a regex to match a ref against :) - next unless $ref =~ /$refex/; + next unless $ref =~ /^$refex/; die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; # as far as *this* ref is concerned we're ok From ecfd20e793df7f63682b412603161d7e1b5ed7e3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 14 Jan 2010 15:14:40 +0530 Subject: [PATCH 210/850] @SHELL is now $SHELL_USERS in the rc file (warning: backward compat breakage) Stop conflating the privilege to push changes to the admin repo with the privilege to get a shell on the server. Please read doc/6 carefully before upgrading to this version. Also please ensure that the gitolite key is *not* your only means to get a command line on the server --- conf/example.conf | 16 ---------------- conf/example.gitolite.rc | 13 +++++++++++++ doc/6-ssh-troubleshooting.mkd | 32 ++++++++++++++++++++++++++++---- src/gl-compile-conf | 4 ++-- src/gl-easy-install | 26 ++++++++++++++++++++------ 5 files changed, 63 insertions(+), 28 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index a9b037c..07f60ab 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -246,19 +246,3 @@ repo gitolite # This does either a plain "git config section.key value" (for the first 3 # examples above) or "git config --unset-all section.key" (for the last # example). Other forms (--add, the value_regex, etc) are not supported. - -# SHELL ACCESS -# ------------ - -# It is possible to give certain users shell access as well as allow them to -# use gitolite features for their git repo access. The idea is to eliminate -# the need for 2 keys when both shell and gitolite access are needed. - -# To give a user shell access, add the username to the special @SHELL group: - -@SHELL = sitaram - -# Do not add people to this group indiscriminately. AUDITABILITY OF ACCESS -# CONTROL CHANGES (AND OF REPO ACCESSES) WILL BE COMPROMISED IF ADMINS CAN -# FIDDLE WITH THE ACTUAL (PLAIN TEXT) LOG FILES THAT GITOLITE KEEPS, WHICH -# THEY CAN EASILY DO IF THEY HAVE A SHELL. diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 4cda90b..d53b65f 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -93,6 +93,19 @@ $PERSONAL=""; $GIT_PATH=""; # $GIT_PATH="/opt/bin/"; +# -------------------------------------- + +# if you want to give shell access to any gitolite user(s), name them here. +# Please see doc/6-ssh-troubleshooting.mkd for details on how this works. + +# Do not add people to this list indiscriminately. AUDITABILITY OF ACCESS +# CONTROL CHANGES (AND OF REPO ACCESSES) WILL BE COMPROMISED IF ADMINS CAN +# FIDDLE WITH THE ACTUAL (PLAIN TEXT) LOG FILES THAT GITOLITE KEEPS, WHICH +# THEY CAN EASILY DO IF THEY HAVE A SHELL. + +# syntax: space separated list of gitolite usernames in *one* string variable. +# $SHELL_USERS = "alice bob"; + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 9602dd2..570e1bd 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -8,10 +8,34 @@ and shell access]...". We've managed (thanks to an idea from Jesse Keating) to get around this. Now it *is* possible for a single key to allow both gitolite access *and* shell access. -This is done by placing such a user in a special `@SHELL` group in the -gitolite config file. As usual, please see `conf/example.conf` for more info -on this, since I'm using that as a central place to document anything -concerned with the conf file. +This is done by: + + * (**on the server**) listing all such users in a variable called + `$SHELL_USERS` in the `~/.gitolite.rc` file. For example: + + $SHELL_USERS = "alice bob"; + + (Note the syntax: a space separated list of users in one string variable). + + * (**on your client**) make at least a dummy change to your clone of the + gitolite-admin repo and push it. + +**IMPORTANT UPGRADE NOTE**: a previous implementation of this feature worked +by adding people to a special group (`@SHELL`) in the *config* file. This +meant that anyone with gitolite-admin repo write access could add himself to +the `@SHELL` group and push, thus obtaining shell. + +This is not a problem for most setups, but if someone wants to separate these +two privileges (the right to push the admin repo and the right to get a shell) +then it does pose a problem. Since the "rc" file can only be edited by +someone who already has shell access, we now use that instead, even though +this forces a change in the syntax. + +To migrate from the old scheme to the new one, add a new variable +`$SHELL_USERS` to `~/.gitolite.rc` on the server with the appropriate names in +it. **It is best to do this directly on the server *before* upgrading to this +version.** (After the upgrade is done and tested you can remove the `@SHELL` +lines from the gitolite config file). ---- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index b654e69..dcd2535 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $SHELL_USERS); # and these are set by gitolite.pm our ($REPONAME_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -458,7 +458,7 @@ for my $pubkey (glob("*")) print STDERR "WARNING: pubkey $pubkey exists but user $user not in config\n" unless $user_list{$user}; $user_list{$user} = 'has pubkey'; - if ($groups{'@SHELL'}{$user}) { + if ($SHELL_USERS and $SHELL_USERS =~ /(^|\s)$user(\s|$)/) { print $newkeys_fh "command=\"$AUTH_COMMAND -s $user\",$AUTH_OPTIONS "; } else { print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS,no-pty "; diff --git a/src/gl-easy-install b/src/gl-easy-install index 6077e23..e8a000b 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -302,12 +302,16 @@ copy_gl() { prompt " ...trying to reuse existing rc" \ "Oh hey... you already had a '.gitolite.rc' file on the server. Let's see if we can use that instead of the default one..." - sort < $tmpgli/.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.old - sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.new - if diff -u $tmpgli/glrc.old $tmpgli/glrc.new + < $tmpgli/.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.old + < conf/example.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.new + comm -13 $tmpgli/glrc.old $tmpgli/glrc.new > $tmpgli/glrc.comm13 + if [[ ! -s $tmpgli/glrc.comm13 ]] then [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc else + echo new variables found in rc file: + cat $tmpgli/glrc.comm13 + echo # MANUAL: if you're upgrading, read the instructions below and # manually make sure your final ~/.gitolite.rc has both your existing # customisations as well as any new variables that the new version of @@ -339,6 +343,8 @@ run_install() { if ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf &> /dev/null then upgrade=1 + ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf 2> /dev/null | grep '@SHELL' && + prompt "" "$v_at_shell_bwi" [[ -n $admin_name ]] && echo -e "\n *** WARNING ***: looks like an upgrade... ignoring argument '$admin_name'" else [[ -z $admin_name ]] && die " *** ERROR ***: doesn't look like an upgrade, so I need a name for the admin" @@ -361,7 +367,6 @@ run_install() { # MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf # and add at least the following lines to it: -# @SHELL = sitaram # repo gitolite-admin # RW+ = sitaram @@ -369,8 +374,6 @@ initial_conf_key() { echo "#gitolite conf # please see conf/example.conf for details on syntax and features -@SHELL = $admin_name - repo gitolite-admin RW+ = $admin_name @@ -543,6 +546,17 @@ next set of command outputs coming up. They're only relevant for a manual install, not this one... " +v_at_shell_bwi=" +you are using the @SHELL feature in your gitolite config. This feature has +now changed in a backward incompatible way; see doc/6-ssh-troubleshooting.mkd +for information on migrating this to the new syntax. + +DO NOT hit enter unless you have understood that information and properly +migrated your setup, or you are sure you have shell access to the server +through some other means than the $admin_name key. + +" + v_done=" done! From d61890301fea2a920d246bafe308a173bcf50369 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 15 Jan 2010 09:27:47 +0530 Subject: [PATCH 211/850] delegation doc: minor oops I know hardly anyone is using delegation, but if you find yourself locked out from pushing because of this one little thing, do this: * on your gitolite-admin clone, add the required lines per this patch, and commit * on the server, edit ~/.gitolite/conf/gitolite.conf-compiled.pm, and delete the following line 'NAME_LIMITS' => 1 from the entry for "gitolite-admin" (if you don't know what that means delete *all* such lines) and save the file * back on your admin repo clone, do a push --- doc/5-delegation.mkd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index 6c62b24..9260046 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -63,6 +63,8 @@ You do this by adding branches to the `gitolite-admin` repo: repo gitolite-admin RW+ = sitaram # now add these lines to the config for the admin repo + RW = alice bob mallory + RW+ NAME/ = sitaram RW NAME/conf/fragments/webbrowser_repos = alice RW NAME/conf/fragments/webserver_repos = bob RW NAME/conf/fragments/malware_repos = mallory From e7962e5eda6bde4137e8a26b9d35df38074436c9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 15 Jan 2010 09:27:47 +0530 Subject: [PATCH 212/850] delegation doc: minor oops I know hardly anyone is using delegation, but if you find yourself locked out from pushing because of this one little thing, do this: * on your gitolite-admin clone, add the required lines per this patch, and commit * on the server, edit ~/.gitolite/conf/gitolite.conf-compiled.pm, and delete the following line 'NAME_LIMITS' => 1 from the entry for "gitolite-admin" (if you don't know what that means delete *all* such lines) and save the file * back on your admin repo clone, do a push --- doc/5-delegation.mkd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index 6c62b24..9260046 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -63,6 +63,8 @@ You do this by adding branches to the `gitolite-admin` repo: repo gitolite-admin RW+ = sitaram # now add these lines to the config for the admin repo + RW = alice bob mallory + RW+ NAME/ = sitaram RW NAME/conf/fragments/webbrowser_repos = alice RW NAME/conf/fragments/webserver_repos = bob RW NAME/conf/fragments/malware_repos = mallory From 261b289609c19143573b599ae10c8951f371874d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 15 Jan 2010 10:40:07 +0530 Subject: [PATCH 213/850] mention NAME-based restrictions in README --- README.mkd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.mkd b/README.mkd index b766f7e..1df3bf8 100644 --- a/README.mkd +++ b/README.mkd @@ -70,6 +70,8 @@ detail [here][gsdiff]. * simpler, yet far more powerful, config file syntax, including specifying gitweb/daemon access. You'll need this power if you manage lots of users+repos+combinations of access + * apart from branch-name based restrictions, you can also restrict by + file/dir name changed (i.e., output of `git diff --name-only`) * config file syntax gets checked upfront, and much more thoroughly * if your requirements are still too complex, you can split up the config file and delegate authority over parts of it From 645ab77af5d3854e1d8c2cef39101c2c5c21f33e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 15 Jan 2010 14:20:00 +0530 Subject: [PATCH 214/850] compile: disallow multiple pubkeys in one file The way pubkey files are handled by gitolite, this could be used by a repo admin to get shell access. It's always been there as an undocumented emergency mechanism for an admin who lost his shell keys or overwrote them due to not understanding ssh well enough (and it has been so used at least once). But not any more... Like the @SHELL case, this reflects a shift away from treating people with repo admin rights as eqvt to people who have shell on the server, and systematically making the former lesser privileged than the latter. While in most cases (including my $DAYJOB) these two may be the same person, I am told that's not a valid assumption for others, and there've been requests to close this potential loophole. --- src/gl-compile-conf | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index dcd2535..141f7d7 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -458,14 +458,20 @@ for my $pubkey (glob("*")) print STDERR "WARNING: pubkey $pubkey exists but user $user not in config\n" unless $user_list{$user}; $user_list{$user} = 'has pubkey'; + # apparently some pubkeys don't end in a newline... + my $pubkey_content = `cat $pubkey`; + $pubkey_content =~ s/\s*$/\n/; + # don't trust files with multiple lines (i.e., something after a newline) + if ($pubkey_content =~ /\n./) + { + print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n"; + next; + } if ($SHELL_USERS and $SHELL_USERS =~ /(^|\s)$user(\s|$)/) { print $newkeys_fh "command=\"$AUTH_COMMAND -s $user\",$AUTH_OPTIONS "; } else { print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS,no-pty "; } - # apparently some pubkeys don't end in a newline... - my $pubkey_content = `cat $pubkey`; - $pubkey_content =~ s/\s*$/\n/; print $newkeys_fh $pubkey_content; } # lint check 3; a little more severe than the first two I guess... From c1de05a8a559827bd5f8f2b037c9e680a9c802fe Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 21 Jan 2010 08:40:26 +0530 Subject: [PATCH 215/850] doc/3: gitweb integ; trailing slash on $projectroot It's not clear whether $projectroot has or does not have a trailing slash. Current code assumes it does, but we need to cater for it not having one also. Otherwise the final reponame ends up with a leading slash, once $projectroot has been stripped from the beginning of the full repo path. --- doc/3-faq-tips-etc.mkd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index a7ee05a..572d06c 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -347,7 +347,7 @@ already done and we just use it! $export_auth_hook = sub { my $reponame = shift; # gitweb passes us the full repo path; so we strip the beginning... - $reponame =~ s/\Q$projectroot//; + $reponame =~ s/\Q$projectroot\E\/?//; # ...and the end, to get the repo name as it is specified in gitolite conf $reponame =~ s/\.git$//; From e68d76f1274150a794a8e6d843569e034556411a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 22 Jan 2010 20:05:14 +0530 Subject: [PATCH 216/850] doc/6 revamp; would appreciate reviews ;-) --- doc/3-faq-tips-etc.mkd | 2 +- doc/6-ssh-troubleshooting.mkd | 280 ++++++++++++++++++++++------------ 2 files changed, 184 insertions(+), 98 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 808cf03..9361099 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -471,7 +471,7 @@ etc. You'd just like a simple way to know what repos you have access to. Easy! Just use ssh and try to log in as if you were attempting to get a shell: - $ ssh gitolite + $ ssh gitolite info PTY allocation request failed on channel 0 hello sitaram, the gitolite version here is v0.6-17-g94ed189 you have the following permissions: diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 570e1bd..b09c09f 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -1,116 +1,175 @@ # ssh troubleshooting -Update 2009-12-23: most of this document is now of historical interest and -will be totally revamped when I have time. For now, just note this amendment. - -The document below says "we can't use the same key for both [gitolite access -and shell access]...". We've managed (thanks to an idea from Jesse Keating) -to get around this. Now it *is* possible for a single key to allow both -gitolite access *and* shell access. - -This is done by: - - * (**on the server**) listing all such users in a variable called - `$SHELL_USERS` in the `~/.gitolite.rc` file. For example: - - $SHELL_USERS = "alice bob"; - - (Note the syntax: a space separated list of users in one string variable). - - * (**on your client**) make at least a dummy change to your clone of the - gitolite-admin repo and push it. - -**IMPORTANT UPGRADE NOTE**: a previous implementation of this feature worked -by adding people to a special group (`@SHELL`) in the *config* file. This -meant that anyone with gitolite-admin repo write access could add himself to -the `@SHELL` group and push, thus obtaining shell. - -This is not a problem for most setups, but if someone wants to separate these -two privileges (the right to push the admin repo and the right to get a shell) -then it does pose a problem. Since the "rc" file can only be edited by -someone who already has shell access, we now use that instead, even though -this forces a change in the syntax. - -To migrate from the old scheme to the new one, add a new variable -`$SHELL_USERS` to `~/.gitolite.rc` on the server with the appropriate names in -it. **It is best to do this directly on the server *before* upgrading to this -version.** (After the upgrade is done and tested you can remove the `@SHELL` -lines from the gitolite config file). - ----- - -Ssh has always been the biggest troublespot in all this. While gitolite makes -it as easy as possible, you might still run into trouble sometimes. - In this document: - * ssh sanity checks - * explanation + * basic ssh troubleshooting + * passphrases versus passwords + * ssh-agent problems + * basic ssh troubleshooting for the main admin + * basic ssh troubleshooting for a normal user + * details * files on the server * files on client * why two keys on client * more complex ssh setups * two gitolite servers to manage? - * further reading + * giving shell access to gitolite users ---- -> But before we get to all that, let's clarify that all this is applicable -> **only** to the gitolite **admin**. He's the only one who needs both a -> shell and gitolite access, so he has **two** pubkeys in play. +This document should help you troubleshoot ssh-related problems in accessing +gitolite *after* the install has completed successfully. -> Normal users have only one pubkey, since they are only allowed to access -> gitolite itself. They do not need to worry about any of this stuff, and -> their repo urls are very simple, like: `git@my.git.server:reponame.git`. +In addition, I **strongly** recommend reading [this document][glb] -- it's a +very detailed look at how gitolite uses ssh's features on the server side. +Most people don't know ssh as well as they *think* they do; even if you dont +have any problems right now, it's worth skimming over. ----- +Please also note that ssh problems don't always look like ssh problems. One +common example: when the remote says the repo you're trying to access "does +not appear to be a git repository", and yet you are sure it exists, you +haven't mis-spelled it, etc. -### ssh sanity checks +### basic ssh troubleshooting -There are two quick sanity checks you can run: +[glb]: http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh - * running `ssh gitolite` should get you a list of repos you have rights to - access, as described [here][myrights] +I assume the gitolite server is called "server" and the user hosting all the +gitolite repos is "git". I will also be using "sitaram" as the *gitolite +username* of the admin. + +Unless specifically mentioned, all these commands are run on the user's or +admin's workstation, not on the server. + +#### passphrases versus passwords + +When you create an ssh keypair, you have the option of protecting it with a +passphrase. When you subsequently use that keypair to access a remote host, +your *local* ssh client needs to unlock the corresponding private key, and ssh +will probably ask for the passphrase you set when you created the keypair. + +Do not confuse or mistake this prompt (`Enter passphrase for key +'/home/sitaram/.ssh/id_rsa':`) for a password prompt from the remote server! + +You have two choices to avoid this prompt every time you try to access the +remote. The first is to create keypairs *without* a passphrase (just hit +enter when prompted for one). **Be sure to add a passphrase later, once +everything is working, using `ssh-keygen -p`**. + +The second is to use `ssh-agent` (or `keychain`, which in turn uses +`ssh-agent`) or something like that to manage your keys. Other than the next +section, further discussion of this is out of scope of this document. + +#### ssh-agent problems + +1. Run `ssh-add -l`. If this responds with either "The agent has no + identities." or "Could not open a connection to your authentication + agent.", skip this section. + +2. However, if it lists some keys, like this: + + 2048 fc:c1:48:1e:06:31:97:a4:8b:fc:37:b2:76:14:c7:53 /home/sitaram/.ssh/id_rsa (RSA) + 2048 d2:e0:7f:fa:1a:89:22:41:bb:06:d9:ff:a7:27:36:5c /home/sitaram/.ssh/sitaram (RSA) + + then run `ls ~/.ssh` and make sure that all the keypairs you have there + are represented in the `ssh-add -l` output. + +3. If you find any keypairs in `~/.ssh` that are not represented in the + `ssh-add -l` output, add them. For instance, if `ssh-add -l` showed me + only the `id_rsa` key, but I also had a `sitaram` (and `sitaram.pub`) + keypair, I'd run `ssh-add ~/.ssh/sitaram` to add it. + +This is because ssh-agent has a quirk: if `ssh-add -l` shows *any* keys at +all, ssh will only use those keys. Even if you explicitly specify an unlisted +key using `ssh -i` or an `identityfile` directive in the config file, it won't +use it. + +#### basic ssh troubleshooting for the main admin + +You're the "main admin" if you're trying to access gitolite from the same +workstation and user account where you ran the "easy install" command. You +should have two keypairs in your `~/.ssh` directory. The pair called `id_rsa` +(and `id_rsa.pub`) was probably the first one you created, and you used this +to get passwordless (pubkey based) access to the server (which was a +pre-requisite for running the easy install command). + +The second keypair has the same name as the last argument in the easy install +command you ran (in my case, `sitaram` and `sitaram.pub`). It was probably +created by the easy install script, and is the key used for gitolite access. + +In addition, you should have a "gitolite" paragraph in your `~/.ssh/config`, +looking something like this: + + host gitolite + user git + hostname server + identityfile ~/.ssh/sitaram + +If any of these are not true, you did something funky in your install; email +me or hop onto #git and hope for the best ;-) + +Otherwise, run these checks: + +1. `ssh git@server` should get you a command line. + + If it asks you for a password, then your `id_rsa` keypair changed after + you ran the easy install, or someone fiddled with the + `~/.ssh/authorized_keys` file on the server. + + If it prints [gitolite version and access info][myrights], you managed to + overwrite the `id_rsa` keypair with the `sitaram` keypair, or something + equally weird. + +2. `ssh gitolite info` should print some [gitolite version and access + info][myrights]. If you get the output of the GNU info command instead, + you probably reused your `id_rsa` keypair as your `sitaram` keypair, or + overwrote the `sitaram` keypair with the `id_rsa` keypair. + +There are many ways to fix this, depending on where and what the damage is. +The most generic way (and therefore time-taking) is to re-install gitolite +from scratch: + + * make a backup of your gitolite-admin repo clone somewhere (basically your + "keydir/*.pub" and your "conf/gitolite.conf"). If necessary get these + files from the server's `~/.gitolite` directory. + * log on to the server somehow (using some other account, using a password, + su-ing in, etc) and delete `~/.ssh/authorized_keys`. Rename or move aside + `~/.gitolite` so that also looks like it is missing. + * back on your workstation, make sure you have 2 keypairs (`id_rsa` and + `sitaram`, along with corresponding `.pub` files). Create them if needed. + Also make sure they are *different* and not a copy of each other :-) + * install gitolite normally: + * run `ssh-copy-id -i ~/.ssh/id_rsa git@server` to get passwordless + access to the server. (Mac users may have to do this step manually) + * make sure `ssh git@server pwd` prints the `$HOME` of `git@server` + **without** asking for a password. Do not proceed till this works. + * run easy install again, (in my case: `cd gitolite-source; + src/gl-easy-install -q git server sitaram`) + * go to your gitolite-admin repo clone, and copy `conf/gitolite.conf` and + `keydir/*.pub` from your backup to this directory + * copy (be sure to overwrite!) `~/.ssh/sitaram.pub` also to keydir + * now `git add keydir; git commit; git push -f` + +That's a long sequence but it should work. + +#### basic ssh troubleshooting for a normal user + +For a normal user, life is much simpler. They should have only one pubkey, +which was previously sent to the gitolite admin to add into the admin repo's +`keydir` as "user.pub", and then "user" given permissions to some repo. + +`ssh git@server info` should get you [gitolite version and access +info][myrights]. If it asks you for a password, your pubkey was not sent to +the server properly. Check with your admin. [myrights]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#myrights - * conversely, `ssh git@server` should get you a command line +If it gets you the GNU info command output, you have shell access. This means +you had command line access to the server *before* you were added as a +gitolite user. If you send that same key to your gitolite admin to include in +the admin repo, it won't work. For reasons why, see below. -If one or both of these does not work as expected, do this: - - * first, check that your `~/.ssh` has two public keys, like below: - - $ ls -al ~/.ssh/*.pub - -rw-r--r-- 1 sitaram sitaram 409 2008-04-21 17:42 /home/sitaram/.ssh/id_rsa.pub - -rw-r--r-- 1 sitaram sitaram 409 2009-10-15 16:25 /home/sitaram/.ssh/sitaram.pub - - If it doesn't you have either lost your keys or you're on the wrong - machine. As long as you have password access to the server you can alweys - recover; just pretend you're installing from scratch and start over. - - * next, try running `ssh-add -l`. On my desktop the output looks like this: - - 2048 63:ea:ab:10:d2:4f:88:f4:85:cb:d3:7d:3a:83:37:9a /home/sitaram/.ssh/id_rsa (RSA) - 2048 d7:23:89:12:5f:22:4f:ad:54:7d:7e:f8:f5:2a:e9:13 /home/sitaram/.ssh/sitaram (RSA) - - If you get only one line (typically the top one), you should ssh-add the - other one, using (in my case) `ssh-add ~/.ssh/sitaram`. - - If you get no output, add both of them and check `ssh-add -l` again. - - If this error keeps happening please consider installing [keychain][kch] - or something similar, or add these commands to your bash startup scripts. - -[kch]: http://www.gentoo.org/proj/en/keychain/ - - * Finally, make sure your `~/.ssh/config` has the required `host gitolite` - para (see below for more on this). - -Once these sanity checks have passed, things should be fine. However, if you -still have problems, make sure that the "origin" URL in any clones looks like -`gitolite:reponame.git`, not `git@server:reponame.git`. - -### explanation +### details Here's how it all hangs together. @@ -301,10 +360,37 @@ instance if you have *two* gitolite servers you are administering)? * now access one server's repos as `gitolite:reponame.git` and the other server's repos as `gitolite2:reponame.git`. -### further reading +### giving shell access to gitolite users -While this focused mostly on the client side ssh, you may also want to read -[this][glb] for a much more detailed explanation of the ssh magic on the -server side. +We've managed (thanks to an idea from Jesse Keating) to make it possible for a +single key to allow both gitolite access *and* shell access. + +This is done by: + + * (**on the server**) listing all such users in a variable called + `$SHELL_USERS` in the `~/.gitolite.rc` file. For example: + + $SHELL_USERS = "alice bob"; + + (Note the syntax: a space separated list of users in one string variable). + + * (**on your client**) make at least a dummy change to your clone of the + gitolite-admin repo and push it. + +**IMPORTANT UPGRADE NOTE**: a previous implementation of this feature worked +by adding people to a special group (`@SHELL`) in the *config* file. This +meant that anyone with gitolite-admin repo write access could add himself to +the `@SHELL` group and push, thus obtaining shell. + +This is not a problem for most setups, but if someone wants to separate these +two privileges (the right to push the admin repo and the right to get a shell) +then it does pose a problem. Since the "rc" file can only be edited by +someone who already has shell access, we now use that instead, even though +this forces a change in the syntax. + +To migrate from the old scheme to the new one, add a new variable +`$SHELL_USERS` to `~/.gitolite.rc` on the server with the appropriate names in +it. **It is best to do this directly on the server *before* upgrading to this +version.** (After the upgrade is done and tested you can remove the `@SHELL` +lines from the gitolite config file). -[glb]: http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh From c8d4aef460e84bfd90e12a9c3f741065f030d2ca Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 23 Jan 2010 14:57:22 +0530 Subject: [PATCH 217/850] compile: allow "#" in *simple* strings like: config notify.ircChannel = "#foo" (thanks, jhelwig) --- src/gl-compile-conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 141f7d7..e88819a 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -162,13 +162,13 @@ sub parse_conf_file my @repos; while (<$conf_fh>) { + # kill comments, but take care of "#" inside *simple* strings + s/^((".*?"|[^#"])*)#.*/$1/; # normalise whitespace; keeps later regexes very simple s/=/ = /; s/\s+/ /g; s/^ //; s/ $//; - # kill comments - s/\s*#.*//; # and blank lines next unless /\S/; From 11e8ab048af553024b32110c41d83dcb235396dd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 25 Jan 2010 09:49:39 +0530 Subject: [PATCH 218/850] doc/6 revamp: minor addition --- doc/6-ssh-troubleshooting.mkd | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index b09c09f..9548792 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -28,7 +28,16 @@ have any problems right now, it's worth skimming over. Please also note that ssh problems don't always look like ssh problems. One common example: when the remote says the repo you're trying to access "does not appear to be a git repository", and yet you are sure it exists, you -haven't mis-spelled it, etc. +haven't mis-spelled it, etc. Another example is being able to access +repositories using the full unix path (typically like +`git@server:repositories/reponame.git`, assuming default `$REPO_BASE` setting, +instead of specifying only the part below `$REPO_BASE`, i.e., +`git@server:reponame.git`). + +[Both these errors indicate that you managed to bypass gitolite completely and +are using your shell access -- instead of running via +`/some/path/gl-auth-command ` it is just going to bash and +working from there!] ### basic ssh troubleshooting From c3ec349721a4e07ecf2d80ef3e49f2e8198a4466 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 25 Jan 2010 12:29:01 +0530 Subject: [PATCH 219/850] sshkeys-lint: new program run without arguments for usage --- doc/6-ssh-troubleshooting.mkd | 4 ++ src/sshkeys-lint | 100 ++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100755 src/sshkeys-lint diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 9548792..8189af8 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -25,6 +25,10 @@ very detailed look at how gitolite uses ssh's features on the server side. Most people don't know ssh as well as they *think* they do; even if you dont have any problems right now, it's worth skimming over. +In addition to both these documents, there's now a program called +`sshkeys-lint` that you can run on your client. Run it without arguments to +get help on how to run it and what inputs it needs. + Please also note that ssh problems don't always look like ssh problems. One common example: when the remote says the repo you're trying to access "does not appear to be a git repository", and yet you are sure it exists, you diff --git a/src/sshkeys-lint b/src/sshkeys-lint new file mode 100755 index 0000000..acb8d72 --- /dev/null +++ b/src/sshkeys-lint @@ -0,0 +1,100 @@ +#!/usr/bin/perl -w + +use strict; +our (%users, %linenos); + +&usage unless $ARGV[0] and -f $ARGV[0]; +my @authlines = &filelines($ARGV[0]); +my $lineno = 0; +for (@authlines) +{ + $lineno++; + if (/^# gitolite start/ .. /^# gitolite end/) { + warn "line $lineno: non-gitolite key found in gitolite section" if /ssh-rsa|ssh-dss/ and not /command=.*gl-auth-command/; + } else { + warn "line $lineno: gitolite key found outside gitolite section" if /command=.*gl-auth-command/; + } + next if /\# gitolite (start|end)/; + die "line $lineno: unrecognised line\n" unless /^(?:command=".*gl-auth-command (\S+?)"\S+ )?(?:ssh-rsa|ssh-dss) (\S+)/; + my ($user, $key) = ($1 || '', $2); + if ($linenos{$key}) { + warn "authkeys file line $lineno is repeat of line $linenos{$key}, will be ignored by server sshd\n"; + next; + } + $linenos{$key} = $lineno; + $users{$key} = ($user ? "maps to gitolite user $user" : "gets you a command line"); +} + +print "\n"; + +# all *.pub in current dir should be exactly one line, starting with ssh-rsa +# or ssh-dss + +my @pubkeys = glob("*.pub"); +die "no *.pub files here\n" unless @pubkeys; +for my $pub (@pubkeys) { + my @lines = &filelines($pub); + die "$pub has more than one line\n" if @lines > 1; + die "$pub does not start with ssh-rsa or ssh-dss\n" unless $lines[0] =~ /^(?:ssh-rsa|ssh-dss) (\S+)/; + my $key = $1; + if ($users{$key}) { + print "$pub $users{$key}\n"; + } else { + print "$pub has NO ACCESS to the server\n"; + } +} + +print <; +} + +sub usage +{ + print STDERR < Date: Mon, 25 Jan 2010 14:36:02 +0530 Subject: [PATCH 220/850] (rats! msysgit doesnt have 'comm'...) --- src/gl-easy-install | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index e8a000b..9887d70 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -304,7 +304,10 @@ copy_gl() { Let's see if we can use that instead of the default one..." < $tmpgli/.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.old < conf/example.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.new - comm -13 $tmpgli/glrc.old $tmpgli/glrc.new > $tmpgli/glrc.comm13 + # msysgit doesn't have "comm". diff is not ideal for our purposes + # because we only care about differences in one direction, but we'll + # have to make do... + diff -u $tmpgli/glrc.old $tmpgli/glrc.new | grep '^+' > $tmpgli/glrc.comm13 if [[ ! -s $tmpgli/glrc.comm13 ]] then [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc @@ -526,7 +529,7 @@ v_upgrade_glrc=" looks like you're upgrading, and there are some new rc variables that this version is expecting that your old rc file doesn't have. -I'm going to run your editor with two filenames. The first is the example +I'm going to run your \\\$EDITOR with two filenames. The first is the example file from this gitolite version. It will have a block (code and comments) for each of the variables shown above with a '+' sign. @@ -536,7 +539,7 @@ it. This is necessary; please dont skip this! -[It's upto you to figure out how your editor handles 2 filename arguments, +[It's upto you to figure out how your \\\$EDITOR handles 2 filename arguments, switch between them, copy lines, etc ;-)] " From 7afaafc54aa84ba614d07f19cb5dc284c296c686 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 27 Jan 2010 16:48:56 +0530 Subject: [PATCH 221/850] document the "include" mechanism --- conf/example.conf | 13 +++++++++++++ doc/3-faq-tips-etc.mkd | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/conf/example.conf b/conf/example.conf index 07f60ab..9d73aca 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -246,3 +246,16 @@ repo gitolite # This does either a plain "git config section.key value" (for the first 3 # examples above) or "git config --unset-all section.key" (for the last # example). Other forms (--add, the value_regex, etc) are not supported. + +# INCLUDE SOME OTHER FILE +# ----------------------- + + include "foo.conf" + +# this includes the contents of $GL_ADMINDIR/conf/foo.conf here + +# Notes: +# - the include statement is not allowed inside delegated fragments for +# security reasons. +# - you can also use an absolute path if you like, although in the interests +# of cloning the admin-repo sanely you should avoid doing this! diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 9361099..8b82086 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -15,6 +15,7 @@ In this document: * two levels of access rights checking * file/dir NAME based restrictions * error checking the config file + * including config lines from other files * delegating parts of the config file * easier to specify gitweb "description" and gitweb/daemon access * easier to link gitweb authorisation with gitolite @@ -254,6 +255,10 @@ was denied. Gitolite "compiles" the config file first and keyword typos *are* caught so you know right away. +#### including config lines from other files + +See the entry under "INCLUDE SOME OTHER FILE" in `conf/example.conf`. + #### delegating parts of the config file You can now split up the config file and delegate the authority to specify From 98d73965b6c42cd7670fcf799e5e9b51177b1a9b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 27 Jan 2010 19:34:37 +0530 Subject: [PATCH 222/850] easy install: two rc file update bugs fixed The "msysgit doesnt have 'comm'" commit (from 2 days ago), had 2 bugs: - (smaller) the "+++" which was part of the diff header was triggering a spurious rc file "new variables" warning, but there were no actual variables to update - (bigger) worse, the grep command, when there were no matches, coupled with the "set -e" to kill the program right there (ouch!) --- src/gl-easy-install | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 9887d70..35c31e2 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -307,7 +307,9 @@ Let's see if we can use that instead of the default one..." # msysgit doesn't have "comm". diff is not ideal for our purposes # because we only care about differences in one direction, but we'll # have to make do... - diff -u $tmpgli/glrc.old $tmpgli/glrc.new | grep '^+' > $tmpgli/glrc.comm13 + set +e + diff -u $tmpgli/glrc.old $tmpgli/glrc.new | grep '^+.*\$' > $tmpgli/glrc.comm13 + set -e if [[ ! -s $tmpgli/glrc.comm13 ]] then [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc From 9c171d166dced181787f22d3abbf1cce8075bb62 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Thu, 28 Jan 2010 22:18:12 +0200 Subject: [PATCH 223/850] "expand" should print to SDTOUT instead of STDERR Other ssh commands where fixed in 15475f666c07e66d91fd00added2a50544d9221b, but "expand" was somehow missed. Signed-off-by: Teemu Matilainen --- src/gitolite.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 74e1bbc..950ad5e 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -245,7 +245,7 @@ sub expand_wild # you need a minimum of "R" access to the regex we're talking about next unless $repos{$repo}{R}{'@all'} or $repos{$repo}{R}{$user}; - print STDERR "($creater)\t$actual_repo\n"; + print "($creater)\t$actual_repo\n"; } } From 4142be4e59f11c7ba6be612b86bba1e5cd3eb439 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 29 Jan 2010 14:39:03 +0530 Subject: [PATCH 224/850] auth: reporting changes for wildcard-created repos - see *all* wildcard repos you have access to (this uses line-anchored regexes as described in doc/4). Examples: ssh git@server expand '.*' ssh git@server expand 'assignment.*' - show perms like the info command does Please see comments against 02cee1d for more details and caveats. --- src/gitolite.pm | 23 ++++++++++++++++++++--- src/gl-auth-command | 3 ++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 950ad5e..cccd310 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -227,6 +227,10 @@ sub expand_wild # access report instead of having to manually change CREATER to his name $repo =~ s/\bCREAT[EO]R\b/$user/g; + # get the list of repo patterns + &parse_acl($GL_CONF_COMPILED, "", "NOBODY", "NOBODY", "NOBODY"); + my @repopatts = grep { $_ !~ $REPONAME_PATT } sort keys %repos; + # display matching repos (from *all* the repos in the system) that $user # has at least "R" access to @@ -237,15 +241,28 @@ sub expand_wild $actual_repo =~ s/\.git$//; # it has to match the pattern being expanded next unless $actual_repo =~ /^$repo$/; + # it also has to match one of the repo patterns in %repos (which we + # already snarfed earlier) + my @patts = grep { $actual_repo =~ /^$_$/ } @repopatts; + # should be exactly one match + # (see reasoning in the "other issues" section of doc/4) + if (@patts != 1) { + # though if it's more than one we print an additional message + print "ignoring $actual_repo; has multiple matches\n(@patts)\n" if @patts > 1; + next; + } # find the creater and subsitute in repos my ($creater, $read, $write) = &repo_rights($repo_base_abs, $actual_repo, $user); # get access list with this &parse_acl($GL_CONF_COMPILED, "", $creater, $read || "NOBODY", $write || "NOBODY"); - # you need a minimum of "R" access to the regex we're talking about - next unless $repos{$repo}{R}{'@all'} or $repos{$repo}{R}{$user}; - print "($creater)\t$actual_repo\n"; + my $perm = ""; + $perm .= ($repos{$patts[0]}{C}{'@all'} or $repos{$patts[0]}{C}{$user}) ? " C" : " "; + $perm .= ($repos{$patts[0]}{R}{'@all'} or $repos{$patts[0]}{R}{$user}) ? " R" : " "; + $perm .= ($repos{$patts[0]}{W}{'@all'} or $repos{$patts[0]}{W}{$user}) ? " W" : " "; + next if $perm eq " "; + print "$perm\t($creater)\t$actual_repo\n"; } } diff --git a/src/gl-auth-command b/src/gl-auth-command index a78c157..f6b2453 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -26,7 +26,7 @@ use warnings; # these are set by the "rc" file our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR); # and these are set by gitolite.pm -our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT); +our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; # the common setup module is in the same directory as this running program is @@ -101,6 +101,7 @@ if ($cmd =~ $CUSTOM_COMMANDS) { } elsif ($verb eq 'expand') { # with a wildcard, you can "expand" it to see what repos actually match + die "$repo has invalid characters" unless "x$repo" =~ $REPOPATT_PATT; expand_wild($GL_CONF_COMPILED, $repo_base_abs, $repo, $user); } else { die "$cmd doesn't make sense to me\n"; From bc0a478e64dcbd7944ebbf894507665ba794ad17 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 30 Jan 2010 04:48:51 +0530 Subject: [PATCH 225/850] auth: minor fix to reporting on wildcard repos Mpenz asked what would happen if the config looked like repo foo/abc R sitaram repo foo/.* RW sitaram If you asked for an expand of '.*', it would pick up permissions from the second set (i.e., "RW") and print them against "foo/abc". This is misleading, since those are not the permissions that will actually be *used*. Gitolite always uses the more specific form if it is given, which means your actual permissions are just "R". This patch is to prevent that misleading reporting in this corner case. --- src/gitolite.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gitolite.pm b/src/gitolite.pm index cccd310..ba72f93 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -230,6 +230,7 @@ sub expand_wild # get the list of repo patterns &parse_acl($GL_CONF_COMPILED, "", "NOBODY", "NOBODY", "NOBODY"); my @repopatts = grep { $_ !~ $REPONAME_PATT } sort keys %repos; + my %reponames = map { $_ => 1 } grep { $_ =~ $REPONAME_PATT } sort keys %repos; # display matching repos (from *all* the repos in the system) that $user # has at least "R" access to @@ -239,6 +240,11 @@ sub expand_wild chomp ($actual_repo); $actual_repo =~ s/^\.\///; $actual_repo =~ s/\.git$//; + # actual_repo should not be present "as is" in the config, because if + # it does, those permissions will override anything inherited from a + # wildcard that also happens to match, and it would be misleading to + # show that here + next if $reponames{$actual_repo}; # it has to match the pattern being expanded next unless $actual_repo =~ /^$repo$/; # it also has to match one of the repo patterns in %repos (which we From b4a65ab73ce7abeaccd5f23c5b0a77d069e029bc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 30 Jan 2010 08:35:43 +0530 Subject: [PATCH 226/850] doc/3: couple of clarifications - deny rules only apply to "W" ops - be more specific about what allows "R" to pass --- doc/3-faq-tips-etc.mkd | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 8b82086..4f305d0 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -217,8 +217,8 @@ Note that at this point no git program has entered the picture, and we have no way of knowing what **ref** (branch, tag, etc) he is trying to update, even if it is a "write" operation. -For a "read" operation to pass this check, the username (or `@all`) must be -mentioned on some line in the config for this repo. +For a "read" operation to pass this check, the username (or `@all`) must have +read permission (i.e., R, RW, or RW+) on at least one branch of the repo. For a "write" operation, there is an additional restriction: lines specifying only `R` (read access) don't count. *The user must have write access to @@ -501,7 +501,9 @@ that code path to better use :-) ***IMPORTANT CAVEAT: if you use deny rules, the order of the rules also makes a difference, where earlier it did not. Please review your ruleset carefully or test it. In particular, do not use `@all` in a deny rule -- it won't work -as you might expect***. +as you might expect***. Also, deny rules are only processed in the second +level checks (see "two levels of access rights checking" above), which means +they only apply to write operations. Take a look at the following snippet, which *seems* to say that "bruce" can write versioned tags (anything containing `refs/tags/v[0-9]`), but the other From 0b960cfae2d89e4bd3cdc1b56823ed34d90e5567 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 31 Jan 2010 23:10:12 +0530 Subject: [PATCH 227/850] auth/update-hook/pm: make &log() a common function --- src/gitolite.pm | 6 ++++++ src/gl-auth-command | 10 ++-------- src/hooks/update | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index d905a72..84dfc9a 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -42,6 +42,12 @@ sub wrap_open { return $fh; } +sub log_it { + open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n"; + print $log_fh @_; + close $log_fh or die "close log failed: $!\n"; +} + # ---------------------------------------------------------------------------- # where is the rc file hiding? # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index d6482ed..f494cc8 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -38,8 +38,7 @@ require "$bindir/gitolite.pm"; &where_is_rc(); die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; -# we need to pass GL_ADMINDIR and the bindir to the child hooks (well only the -# admin repo's post-update hook but still...) +# we need to pass GL_ADMINDIR and the bindir to the child hooks $ENV{GL_ADMINDIR} = $GL_ADMINDIR; $ENV{GL_BINDIR} = $bindir; @@ -149,12 +148,7 @@ $GL_LOGT =~ s/%m/$m/g; $GL_LOGT =~ s/%d/$d/g; $ENV{GL_LOG} = $GL_LOGT; -# if log failure isn't important enough to block access, get rid of all the -# error checking -open my $log_fh, ">>", $ENV{GL_LOG} - or die "open log failed: $!\n"; -print $log_fh "$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n"; -close $log_fh or die "close log failed: $!\n"; +&log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n"); # ---------------------------------------------------------------------------- # over to git now diff --git a/src/hooks/update b/src/hooks/update index c1ff18f..a68ce18 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -33,6 +33,10 @@ die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; # then "do" the compiled config file, whose name we now know die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; +# we've started to need some common subs in what used to be a small, cute, +# little script that barely spanned a few lines :( +require "$ENV{GL_BINDIR}/gitolite.pm"; + # ---------------------------------------------------------------------------- # start... # ---------------------------------------------------------------------------- @@ -118,11 +122,7 @@ check_ref($_) for @refs; # if we returned at all, all the checks succeeded, so we log the action and exit 0 -# logging note: if log failure isn't important enough to block pushes, get rid -# of all the error checking -open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n"; -print $log_fh "$ENV{GL_TS} $perm\t" . +&log_it("$ENV{GL_TS} $perm\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . - "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\t$log_refex\n"; -close $log_fh or die "close log failed: $!\n"; + "\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\t$log_refex\n"); exit 0; From 7f203fc020858a01c39caa2a45587c6b3efa836e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 31 Jan 2010 23:56:58 +0530 Subject: [PATCH 228/850] update-hook/pm: made check_ref a common sub --- src/gitolite.pm | 26 ++++++++++++++++++++++++++ src/hooks/update | 32 ++------------------------------ 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 84dfc9a..8ad5567 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -48,6 +48,32 @@ sub log_it { close $log_fh or die "close log failed: $!\n"; } +# check one ref +sub check_ref { + + # normally, the $ref will be whatever ref the commit is trying to update + # (like refs/heads/master or whatever). At least one of the refexes that + # pertain to this user must match this ref **and** the corresponding + # permission must also match the action (W or +) being attempted. If none + # of them match, the access is denied. + + # Notice that the function DIES!!! Any future changes that require more + # work to be done *after* this, even on failure, can start using return + # codes etc., but for now we're happy to just die. + + my ($allowed_refs, $repo, $ref, $perm) = @_; + for my $ar (@{$allowed_refs}) { + my $refex = (keys %$ar)[0]; + # refex? sure -- a regex to match a ref against :) + next unless $ref =~ /^$refex/; + die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; + + # as far as *this* ref is concerned we're ok + return $refex if ($ar->{$refex} =~ /\Q$perm/); + } + die "$perm $ref $repo $ENV{GL_USER} DENIED by fallthru\n"; +} + # ---------------------------------------------------------------------------- # where is the rc file hiding? # ---------------------------------------------------------------------------- diff --git a/src/hooks/update b/src/hooks/update index a68ce18..876f46b 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -85,40 +85,12 @@ if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) { push @refs, map { chomp; s/^/NAME\//; $_; } `git diff --name-only $oldtree $newtree`; } -my $refex = ''; - -# check one ref -sub check_ref { - - # normally, the $ref will be whatever ref the commit is trying to update - # (like refs/heads/master or whatever). At least one of the refexes that - # pertain to this user must match this ref **and** the corresponding - # permission must also match the action (W or +) being attempted. If none - # of them match, the access is denied. - - # Notice that the function DIES!!! Any future changes that require more - # work to be done *after* this, even on failure, can start using return - # codes etc., but for now we're happy to just die. - - my $ref = shift; - for my $ar (@allowed_refs) { - $refex = (keys %$ar)[0]; - # refex? sure -- a regex to match a ref against :) - next unless $ref =~ /^$refex/; - die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; - - # as far as *this* ref is concerned we're ok - return $refex if ($ar->{$refex} =~ /\Q$perm/); - } - die "$perm $ref $ENV{GL_REPO} $ENV{GL_USER} DENIED by fallthru\n"; -} - # and in this version, we have many "refs" to check. The one we print in the # log is the *first* one (which is a *real* ref, like refs/heads/master), # while all the rest (if they exist) are like NAME/something. So we do the # first one separately to capture it, then run the rest (if any) -my $log_refex = check_ref(shift @refs); -check_ref($_) for @refs; +my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, (shift @refs), $perm); +&check_ref (\@allowed_refs, $ENV{GL_REPO}, $_ , $perm) for @refs; # if we returned at all, all the checks succeeded, so we log the action and exit 0 From 98a4c79dce63ff0a7663d9f677f1a7339cb7c4fe Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 31 Jan 2010 20:24:36 +0530 Subject: [PATCH 229/850] (read this in full) access control for non-git commands running over ssh This is actually a pretty big deal, and I am seriously starting wonder if calling this "gito*lite*" is justified anymore. Anyway, in for a penny, in for a pound... This patch implements a generic way to allow access control for external commands, as long as they are invoked via ssh and present a server-side command that contains enough information to make an access control decision. The first (and only, so far) such command implemented is rsync. Please read the changes in this commit (at least the ones in conf/ and doc/) carefully. --- README.mkd | 5 +++ conf/example.conf | 18 ++++++++++ conf/example.gitolite.rc | 11 ++++++ doc/3-faq-tips-etc.mkd | 15 ++++++++ src/gitolite.pm | 78 ++++++++++++++++++++++++++++++++++++++++ src/gl-auth-command | 7 ++-- src/gl-compile-conf | 1 + 7 files changed, 132 insertions(+), 3 deletions(-) diff --git a/README.mkd b/README.mkd index 1df3bf8..7f3956a 100644 --- a/README.mkd +++ b/README.mkd @@ -99,6 +99,11 @@ or in Unix, perl, shell, etc.)... well I can't afford 1000 USD rewards like djb, so you'll have to settle for 1000 INR (Indian Rupees) as a "token" prize :-) +Update 2010-01-31: this security promise does not apply if you enable any of +the external command helpers (like rsync). It's probably quite secure, but I +just haven't thought about it enough to be able to make such promises, like I +can for the rest of "master". + ---- ### contact and license diff --git a/conf/example.conf b/conf/example.conf index 9d73aca..6dcc4e2 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -259,3 +259,21 @@ repo gitolite # security reasons. # - you can also use an absolute path if you like, although in the interests # of cloning the admin-repo sanely you should avoid doing this! + +# EXTERNAL COMMAND HELPERS -- RSYNC +# --------------------------------- + +# If $RSYNC_BASE is non-empty, the following config entries come into play +# (otherwise they are ignored): + +# a "fake" git repository to collect rsync rules. Gitolite does not +# auto-create any repo whose name starts with EXTCMD/ +repo EXTCMD/rsync +# grant permissions to files/dirs within the $RSYNC_BASE tree. A leading +# NAME/ is required as a prefix; the actual path starts after that. Matching +# follows the same rules as elsewhere in gitolite. + RW NAME/ = sitaram + RW NAME/foo/ = user1 + R NAME/bar/ = user2 +# just to remind you that these are perl regexes, not shell globs + RW NAME/baz/.*/*.c = user3 diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index d53b65f..cf7fe3d 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -106,6 +106,17 @@ $GIT_PATH=""; # syntax: space separated list of gitolite usernames in *one* string variable. # $SHELL_USERS = "alice bob"; +# -------------------------------------- + +# EXTERNAL COMMAND HELPER -- RSYNC +# +# base path of all the files that are accessible via rsync. Must be an +# absolute path. Leave it undefined or set to the empty string to disable the +# rsync helper. +$RSYNC_BASE = ""; +# $RSYNC_BASE = "/home/git/up-down"; +# $RSYNC_BASE = "/tmp/up-down"; + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index cd3bdb7..edc6351 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -26,6 +26,7 @@ In this document: * "exclude" (or "deny") rules * "personal" branches * custom hooks and custom git config + * access control for external commands * design choices * keeping the parser and the access control separate @@ -608,6 +609,20 @@ You can specify hooks that you want to propagate to all repos, as well as per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and `conf/example.conf` for details. +#### access control for external commands + +Gitolite now has a mechanism for allowing access control for arbitrary +external commands, as long as they are invoked via ssh and present a +server-side command that contains enough information to make an access control +decision. The first (and only, so far) such command implemented is rsync. + +Note that this is incompatible with giving people shell access as described in +`doc/6-ssh-troubleshooting.mkd` -- people who have shell access are not +subject to this mechanism (it wouldn't make sense to try and control someone +who has shell access anyway). + +Please see the config files (both of them) for examples and usage. + ### design choices #### keeping the parser and the access control separate diff --git a/src/gitolite.pm b/src/gitolite.pm index 8ad5567..c5bcd9e 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -156,4 +156,82 @@ sub report_basic print "$perm\t$r\n\r" if $perm; } } + +# ---------------------------------------------------------------------------- +# E X T E R N A L C O M M A N D H E L P E R S +# ---------------------------------------------------------------------------- + +sub ext_cmd +{ + my ($GL_CONF_COMPILED, $RSYNC_BASE, $cmd) = @_; + + # check each external command we know about and call it if enabled + if ($RSYNC_BASE and $cmd =~ /^rsync /) { + &ext_cmd_rsync($GL_CONF_COMPILED, $RSYNC_BASE, $cmd); + } else { + die "bad command: $cmd\n"; + } +} + +# ---------------------------------------------------------------------------- +# generic check access routine +# ---------------------------------------------------------------------------- + +sub check_access +{ + my ($GL_CONF_COMPILED, $repo, $path, $perm) = @_; + my $ref = "NAME/$path"; + + &parse_acl($GL_CONF_COMPILED); + + # until I do some major refactoring (which will bloat the update hook a + # bit, sadly), this code duplicates stuff in the current update hook. + + my @allowed_refs; + # we want specific perms to override @all, so they come first + push @allowed_refs, @ { $repos{$repo}{$ENV{GL_USER}} || [] }; + push @allowed_refs, @ { $repos{$repo}{'@all'} || [] }; + + for my $ar (@allowed_refs) { + my $refex = (keys %$ar)[0]; + next unless $ref =~ /^$refex/; + die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; + return if ($ar->{$refex} =~ /\Q$perm/); + } + die "$perm $ref $ENV{GL_REPO} $ENV{GL_USER} DENIED by fallthru\n"; +} + +# ---------------------------------------------------------------------------- +# external command helper: rsync +# ---------------------------------------------------------------------------- + +sub ext_cmd_rsync +{ + my ($GL_CONF_COMPILED, $RSYNC_BASE, $cmd) = @_; + + # test the command patterns; reject if they don't fit. Rsync sends + # commands that looks like one of these to the server (the first one is + # for a read, the second for a write) + # rsync --server --sender -some.flags . some/path + # rsync --server -some.flags . some/path + + die "bad rsync command: $cmd" + unless $cmd =~ /^rsync --server( --sender)? -[\w.]+ \. (\S+)$/; + my $perm = "W"; + $perm = "R" if $1; + my $path = $2; + die "I dont like absolute paths in $cmd\n" if $path =~ /^\//; + die "I dont like '..' paths in $cmd\n" if $path =~ /\.\./; + + # ok now check if we're permitted to execute a $perm action on $path + # (taken as a refex) using rsync. + + &check_access($GL_CONF_COMPILED, 'EXTCMD/rsync', $path, $perm); + # that should "die" if there's a problem + + wrap_chdir($RSYNC_BASE); + exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND}; +} + + 1; diff --git a/src/gl-auth-command b/src/gl-auth-command index f494cc8..1dd2fba 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -24,7 +24,7 @@ use warnings; # ---------------------------------------------------------------------------- # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT); our %repos; @@ -99,8 +99,9 @@ my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/); unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) { # if the user is allowed a shell, just run the command exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND} if $shell_allowed; - # otherwise, whine - die "bad command: $cmd\n"; + # otherwise, call the external command helper + &ext_cmd($GL_CONF_COMPILED, $RSYNC_BASE, $cmd); + exit; # in case the external command helper forgot :-) } # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index e88819a..0a8d369 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -355,6 +355,7 @@ my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" wrap_chdir("$repo_base_abs"); for my $repo (sort keys %repos) { + next if $repo =~ m(^EXTCMD/); # these are not real repos unless (-d "$repo.git") { new_repo($repo, "$GL_ADMINDIR/src/hooks"); # new_repo would have chdir'd us away; come back From 18312de77ab6f85ce2e7535d47041717deb85912 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 31 Jan 2010 21:09:05 +0530 Subject: [PATCH 230/850] rsync: add support for delete/partial --- src/gitolite.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index c5bcd9e..54dd1da 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -216,7 +216,7 @@ sub ext_cmd_rsync # rsync --server -some.flags . some/path die "bad rsync command: $cmd" - unless $cmd =~ /^rsync --server( --sender)? -[\w.]+ \. (\S+)$/; + unless $cmd =~ /^rsync --server( --sender)? -[\w.]+(?: --(?:delete|partial))* \. (\S+)$/; my $perm = "W"; $perm = "R" if $1; my $path = $2; From 20c29c01450fc4b4f69a75ad93c50602a32262e2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 1 Feb 2010 11:35:35 +0530 Subject: [PATCH 231/850] rsync: log the command used --- src/gitolite.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gitolite.pm b/src/gitolite.pm index 54dd1da..c6d75de 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -230,6 +230,7 @@ sub ext_cmd_rsync # that should "die" if there's a problem wrap_chdir($RSYNC_BASE); + &log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{USER}\n"); exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND}; } From 43da598c0852c4dcbd78fdb7251ed338c14fe5ce Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 1 Feb 2010 11:36:24 +0530 Subject: [PATCH 232/850] auth: minor flow change when defaulting to "info" --- src/gl-auth-command | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 1dd2fba..5b3dc42 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -67,16 +67,16 @@ my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! # sanity checks on SSH_ORIGINAL_COMMAND # ---------------------------------------------------------------------------- -# print basic access info if SSH_ORIGINAL_COMMAND does not exist +# no SSH_ORIGINAL_COMMAND given... unless ($ENV{SSH_ORIGINAL_COMMAND}) { - # unless the user is allowed to use a shell + # if the user is allowed to use a shell, give him one if ($shell_allowed) { my $shell = $ENV{SHELL}; $shell =~ s/.*\//-/; # change "/bin/bash" to "-bash" exec { $ENV{SHELL} } $shell; } - &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); - exit 1; + # otherwise, pretend he typed in "info" and carry on... + $ENV{SSH_ORIGINAL_COMMAND} = 'info'; } my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; From 09195afd443e3fb92b5c8f62fb3971a176d09c31 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 31 Jan 2010 11:43:43 +0530 Subject: [PATCH 233/850] document deny rules a bit better --- conf/example.conf | 20 ++++++++++++-------- doc/3-faq-tips-etc.mkd | 9 +++------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 6dcc4e2..6ad91ad 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -142,10 +142,18 @@ repo git # DENY/EXCLUDE RULES -# ***IMPORTANT NOTE: if you use deny rules, the order of the rules also makes -# a difference, where earlier it did not. Please review your ruleset -# carefully or test it. In particular, do not use `@all` in a deny rule -- it -# won't work as you might expect***. +# ***IMPORTANT NOTES ABOUT "DENY" RULES***: + +# - deny rules do NOT affect read access. They only apply to `W` and `+`. +# +# - when using deny rules, the order of your rules starts to matter, where +# earlier it did not. The first matching rule applies, where "matching" is +# defined as either permitting the operation you're attempting (`W` or `+`), +# which results in success, or a "deny" (`-`), which results in failure. +# (As before, a fallthrough also results in failure). +# +# - do not use `@all` when your config has any deny rules; it won't work as +# you probably expect it to! # in the example above, you cannot easily say "anyone can write any tag, # except version tags can only be written by junio". The following might look @@ -161,10 +169,6 @@ repo git - refs/tags/v[0-9] = linus pasky @others RW refs/tags/ = junio linus pasky @others -# Briefly, the rule is: the first matching refex that has the operation you're -# looking for (`W` or `+`), or a minus (`-`), results in success, or failure, -# respectively. A fallthrough also results in failure - # FILE/DIR NAME BASED RESTRICTIONS # -------------------------------- diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index edc6351..67ef956 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -499,12 +499,9 @@ that code path to better use :-) #### "exclude" (or "deny") rules -***IMPORTANT CAVEAT: if you use deny rules, the order of the rules also makes -a difference, where earlier it did not. Please review your ruleset carefully -or test it. In particular, do not use `@all` in a deny rule -- it won't work -as you might expect***. Also, deny rules are only processed in the second -level checks (see "two levels of access rights checking" above), which means -they only apply to write operations. +Here is an illustrative explanation of "deny" rules. However, please be sure +to read the "DENY/EXCLUDE RULES" section in `conf/example.conf` for important +notes/caveats before using "deny" rules. Take a look at the following snippet, which *seems* to say that "bruce" can write versioned tags (anything containing `refs/tags/v[0-9]`), but the other From 2d9c4c4ae9e04b9ce0fa91cb1e5857ad541858d6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 1 Feb 2010 16:54:39 +0530 Subject: [PATCH 234/850] oops; logging bug --- src/gl-auth-command | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 5b3dc42..8fae9f5 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -63,6 +63,24 @@ if ($ARGV[0] eq '-s') { # first, fix the biggest gripe I have with gitosis, a 1-line change my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! +# ---------------------------------------------------------------------------- +# logging, timestamp env vars +# ---------------------------------------------------------------------------- + +# timestamp +my ($s, $min, $h, $d, $m, $y) = (localtime)[0..5]; +$y += 1900; $m++; # usual adjustments +for ($s, $min, $h, $d, $m) { + $_ = "0$_" if $_ < 10; +} +$ENV{GL_TS} = "$y-$m-$d.$h:$min:$s"; + +# substitute template parameters and set the logfile name +$GL_LOGT =~ s/%y/$y/g; +$GL_LOGT =~ s/%m/$m/g; +$GL_LOGT =~ s/%d/$d/g; +$ENV{GL_LOG} = $GL_LOGT; + # ---------------------------------------------------------------------------- # sanity checks on SSH_ORIGINAL_COMMAND # ---------------------------------------------------------------------------- @@ -108,6 +126,8 @@ unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo an # first level permissions check # ---------------------------------------------------------------------------- +$ENV{GL_REPO}=$repo; + # parse the compiled acl; goes into %repos (global) &parse_acl($GL_CONF_COMPILED); @@ -128,32 +148,11 @@ if ( not -d "$repo_base_abs/$repo.git" ) { } } -# ---------------------------------------------------------------------------- -# logging, timestamp. also setup env vars for later -# ---------------------------------------------------------------------------- - -# reponame -$ENV{GL_REPO}=$repo; - -# timestamp -my ($s, $min, $h, $d, $m, $y) = (localtime)[0..5]; -$y += 1900; $m++; # usual adjustments -for ($s, $min, $h, $d, $m) { - $_ = "0$_" if $_ < 10; -} -$ENV{GL_TS} = "$y-$m-$d.$h:$min:$s"; - -# substitute template parameters and set the logfile name -$GL_LOGT =~ s/%y/$y/g; -$GL_LOGT =~ s/%m/$m/g; -$GL_LOGT =~ s/%d/$d/g; -$ENV{GL_LOG} = $GL_LOGT; - -&log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n"); - # ---------------------------------------------------------------------------- # over to git now # ---------------------------------------------------------------------------- +&log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n"); + $repo = "'$REPO_BASE/$repo.git'"; exec("git", "shell", "-c", "$verb $repo"); From b1659db7428fb69e9ec32df428154a88847f24d2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 1 Feb 2010 22:55:42 +0530 Subject: [PATCH 235/850] more fixes to wildcard reporting... (thank God I don't warrant this part of gitolite ;-) --- src/gitolite.pm | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index f0e37a6..045e339 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -261,7 +261,6 @@ sub expand_wild # get the list of repo patterns &parse_acl($GL_CONF_COMPILED, "", "NOBODY", "NOBODY", "NOBODY"); - my @repopatts = grep { $_ !~ $REPONAME_PATT } sort keys %repos; my %reponames = map { $_ => 1 } grep { $_ =~ $REPONAME_PATT } sort keys %repos; # display matching repos (from *all* the repos in the system) that $user @@ -279,26 +278,15 @@ sub expand_wild next if $reponames{$actual_repo}; # it has to match the pattern being expanded next unless $actual_repo =~ /^$repo$/; - # it also has to match one of the repo patterns in %repos (which we - # already snarfed earlier) - my @patts = grep { $actual_repo =~ /^$_$/ } @repopatts; - # should be exactly one match - # (see reasoning in the "other issues" section of doc/4) - if (@patts != 1) { - # though if it's more than one we print an additional message - print "ignoring $actual_repo; has multiple matches\n(@patts)\n" if @patts > 1; - next; - } # find the creater and subsitute in repos my ($creater, $read, $write) = &repo_rights($repo_base_abs, $actual_repo, $user); # get access list with this - &parse_acl($GL_CONF_COMPILED, "", $creater, $read || "NOBODY", $write || "NOBODY"); + &parse_acl($GL_CONF_COMPILED, $actual_repo, $creater, $read || "NOBODY", $write || "NOBODY"); - my $perm = ""; - $perm .= ($repos{$patts[0]}{C}{'@all'} or $repos{$patts[0]}{C}{$user}) ? " C" : " "; - $perm .= ($repos{$patts[0]}{R}{'@all'} or $repos{$patts[0]}{R}{$user}) ? " R" : " "; - $perm .= ($repos{$patts[0]}{W}{'@all'} or $repos{$patts[0]}{W}{$user}) ? " W" : " "; + my $perm = " "; + $perm .= ($repos{$actual_repo}{R}{'@all'} or $repos{$actual_repo}{R}{$user}) ? " R" : " "; + $perm .= ($repos{$actual_repo}{W}{'@all'} or $repos{$actual_repo}{W}{$user}) ? " W" : " "; next if $perm eq " "; print "$perm\t($creater)\t$actual_repo\n"; } From 0a7fa6c6b58b8c64e1c866f8781fbb5140b6f750 Mon Sep 17 00:00:00 2001 From: "martin f. krafft" Date: Wed, 3 Feb 2010 21:13:47 +1300 Subject: [PATCH 236/850] Tell gitweb about repo owner via git-config Gitolite uses projects.list to set the owners for gitweb's use. Unfortunately, this does not work for gitweb setups that set $projectroot to a directory, thus generating the list of repositories on the fly. This patch changes that: gitolite now writes the gitweb.owner configuration variable for each repository (and properly cleans up after itself if the owner is removed). The patch causes gitolite not to write the owner to projects.list anymore, as this would be redundant. The owner also needs no longer be escaped, so this patch removes the poor man's 's/ /+/g' escaping previously in place. Note that I am not a Perl coder. Thus there are probably better ways to implement this, but at least it works. Cc: Sitaram Chamarty Signed-off-by: martin f. krafft --- src/gl-compile-conf | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 0a8d369..bb05d1e 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -284,7 +284,6 @@ sub parse_conf_file die "$WARN $fragment attempting to set description for $repo\n" if $fragment ne 'master' and $fragment ne $repo and ($groups{"\@$fragment"}{$repo} || '') ne 'master'; $desc{"$repo.git"} = $desc; - $owner =~ s/ /+/g if $owner; # gitweb/INSTALL wants more, but meh...! $owner{"$repo.git"} = $owner || ''; } else @@ -415,16 +414,32 @@ for my $repo (sort keys %repos) { $projlist{"$repo.git"} = 1; # add the description file; no messages to user or error checking :) $desc{"$repo.git"} and open(DESC, ">", $desc_file) and print DESC $desc{"$repo.git"} . "\n" and close DESC; + if ($owner{"$repo.git"}) { + # set the repository owner + system("git", "--git-dir=$repo.git", "config", "gitweb.owner", $owner{"$repo.git"}); + } else { + # remove the repository owner setting + system("git --git-dir=$repo.git config --unset-all gitweb.owner 2>/dev/null"); + } } else { # delete the description file; no messages to user or error checking :) unlink $desc_file; + # remove the repository owner setting + system("git --git-dir=$repo.git config --unset-all gitweb.owner 2>/dev/null"); + } + + # unless there are other gitweb.* keys set, remove the section to keep the + # config file clean + my $keys = `git --git-dir=$repo.git config --get-regexp '^gitweb\\.' 2>/dev/null`; + if (length($keys) == 0) { + system("git --git-dir=$repo.git config --remove-section gitweb 2>/dev/null"); } } # update the project list my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); for my $proj (sort keys %projlist) { - print $projlist_fh "$proj" . ( $owner{$proj} ? " $owner{$proj}" : "" ) . "\n"; + print $projlist_fh "$proj\n"; } close $projlist_fh; From 67c10a34fe4d934ae92cc9c8320a396c657d8344 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 1 Feb 2010 15:37:35 +0530 Subject: [PATCH 237/850] auth: new subcommand "htpasswd" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit great idea by Robin Smidsrød: since users are already capable of authenticating themselves to gitolite via ssh keys, use that to let them set or change their own HTTP passwords (ie, run the "htpasswd" command with the correct parameters on behalf of the "git" user on the server) code, rc para, and documentation. In fact everything except... ahem... testing ;-) and while we're about it, we also reorganised the way these helper commands (including the venerable "info" are called) --- conf/example.gitolite.rc | 9 ++++++++ doc/3-faq-tips-etc.mkd | 29 +++++++++++++++++++------ src/gitolite.pm | 46 +++++++++++++++++++++++++++++++++++----- src/gl-auth-command | 42 ++++++++++++++++++------------------ 4 files changed, 94 insertions(+), 32 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index cf7fe3d..bcb281f 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -108,6 +108,15 @@ $GIT_PATH=""; # -------------------------------------- +# if you want to enable the "htpasswd" command, give this the absolute path to +# whatever file apache (etc) expect to find the passwords in. + +$HTPASSWD_FILE = ""; + +# Look in doc/3 ("easier to link gitweb authorisation with gitolite" section) +# for more details on using this feature. + +# -------------------------------------- # EXTERNAL COMMAND HELPER -- RSYNC # # base path of all the files that are accessible via rsync. Must be an diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 67ef956..6723000 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -331,9 +331,25 @@ This requires that: * the HTTP auth should use the same username (like "sitaram") as used in the gitolite config (for the corresponding user) -Once that is done, it's easy. Gitweb allows you to specify a subroutine to -decide on access. We use that feature and tie it to gitolite. Sample code -(untested, munged from something I saw [here][leho]) is given below. +Normally a superuser sets up passwords for users using the "htpasswd" command, +but this is an administrative chore. + +Robin Smidsrød had the *great* idea that, since each user already has pubkey +access to `git@server`, this gives us a very neat way of using gitolite to let +the users *manage their own HTTP passwords*. Here's how: + + * setup apache so that the htaccess file it looks for is owned by the "git" + user + * in the `~/.gitolite.rc` file, look for the variable `$HTPASSWD_FILE` and + point it to this file + * tell your users to type in `ssh git@server htpasswd` to set or change + their HTTP passwords + +Here's the rest of how it hangs together. + +Gitweb allows you to specify a subroutine to decide on access. We use that +feature and tie it to gitolite. Sample code (untested by me, but others do +use it, munged from something I saw [here][leho]) is given below. Note the **utter simplicity** of the actual check (just 1 line!). This is an unexpected piece of luck coming from the decision to keep the config parse @@ -349,7 +365,7 @@ already done and we just use it! $projectroot = '/home/git/repositories/'; my $gl_conf_compiled = '/home/git/.gitolite/conf/gitolite.conf-compiled.pm'; - # I assume this gives us the HTTP auth username + # I am told this gives us the HTTP auth username my $username = $cgi->remote_user; # ---------- @@ -359,10 +375,11 @@ already done and we just use it! die "parse $gl_conf_compiled failed: " . ($! or $@) unless do $gl_conf_compiled; # this is gitweb's mechanism; it calls whatever sub is pointed at by this - # variable to decide access yes/no + # variable to decide access yes/no. Gitweb calls it with one argument + # containing the full path of the repo being accessed $export_auth_hook = sub { my $reponame = shift; - # gitweb passes us the full repo path; so we strip the beginning... + # take the full path provided, strip the beginning... $reponame =~ s/\Q$projectroot\E\/?//; # ...and the end, to get the repo name as it is specified in gitolite conf $reponame =~ s/\.git$//; diff --git a/src/gitolite.pm b/src/gitolite.pm index c6d75de..a8127e9 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -158,17 +158,28 @@ sub report_basic } # ---------------------------------------------------------------------------- -# E X T E R N A L C O M M A N D H E L P E R S +# S P E C I A L C O M M A N D S # ---------------------------------------------------------------------------- -sub ext_cmd +sub special_cmd { - my ($GL_CONF_COMPILED, $RSYNC_BASE, $cmd) = @_; + my ($GL_ADMINDIR, $GL_CONF_COMPILED, $RSYNC_BASE, $HTPASSWD_FILE) = @_; - # check each external command we know about and call it if enabled - if ($RSYNC_BASE and $cmd =~ /^rsync /) { + my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; + my $user = $ENV{GL_USER}; + + # check each special command we know about and call it if enabled + if ($cmd eq 'info') { + &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); + print "you also have shell access\n\r" if $shell_allowed; + } elsif ($HTPASSWD_FILE and $cmd eq 'htpasswd') { + &ext_cmd_htpasswd($HTPASSWD_FILE); + } elsif ($RSYNC_BASE and $cmd =~ /^rsync /) { &ext_cmd_rsync($GL_CONF_COMPILED, $RSYNC_BASE, $cmd); } else { + # if the user is allowed a shell, just run the command + exec $ENV{SHELL}, "-c", $cmd if $shell_allowed; + die "bad command: $cmd\n"; } } @@ -234,5 +245,30 @@ sub ext_cmd_rsync exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND}; } +# ---------------------------------------------------------------------------- +# external command helper: htpasswd +# ---------------------------------------------------------------------------- + +sub ext_cmd_htpasswd +{ + my $HTPASSWD_FILE = shift; + + die "$HTPASSWD_FILE doesn't exist or is not writable\n" unless -w $HTPASSWD_FILE; + $|++; + print <; + $password =~ s/[\n\r]*$//; + my $rc = system("htpasswd", "-b", $HTPASSWD_FILE, $ENV{GL_USER}, $password); + die "htpasswd command seems to have failed with $rc return code...\n" if $rc; +} 1; diff --git a/src/gl-auth-command b/src/gl-auth-command index 8fae9f5..8aa2f65 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -24,7 +24,7 @@ use warnings; # ---------------------------------------------------------------------------- # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT); our %repos; @@ -97,31 +97,31 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { $ENV{SSH_ORIGINAL_COMMAND} = 'info'; } -my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; -# people allowed to get a shell can get basic access info by asking nicely -if ($cmd eq 'info') { - &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); - print "you also have shell access\n\r" if $shell_allowed; - exit 1; -} +# ---------------------------------------------------------------------------- +# non-git commands +# ---------------------------------------------------------------------------- -# split into command and arguments; the pattern allows old style as well as -# new style: "git-subcommand arg" or "git subcommand arg", just like gitosis -# does, although I'm not sure how necessary that is -# -# keep in mind this is how git sends across the command: -# git-receive-pack 'reponame.git' -# including the single quotes +# if the command does NOT fit the pattern of a normal git command, send it off +# somewhere else... -my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/); +# side notes on detecting a normal git command: the pattern we check allows +# old style as well as new style ("git-subcommand arg" or "git subcommand +# arg"), just like gitosis does, although I'm not sure how necessary that is. +# Currently, this is how git sends across the command (including the single +# quotes): +# git-receive-pack 'reponame.git' + +my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/); unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) { - # if the user is allowed a shell, just run the command - exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND} if $shell_allowed; - # otherwise, call the external command helper - &ext_cmd($GL_CONF_COMPILED, $RSYNC_BASE, $cmd); - exit; # in case the external command helper forgot :-) + # ok, it's not a normal git command; call the special command helper + &special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $RSYNC_BASE, $HTPASSWD_FILE); + exit; } +# ---------------------------------------------------------------------------- +# the real git commands (git-receive-pack, etc...) +# ---------------------------------------------------------------------------- + # ---------------------------------------------------------------------------- # first level permissions check # ---------------------------------------------------------------------------- From 86166f7adc60f191b145818eda56d095aca7ed7f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 4 Feb 2010 15:16:47 +0530 Subject: [PATCH 238/850] $shell_allowed needs to be passed to specal_cmds brought on by realising that you lost $shell_allowed when refactoring (previous commit) but perl hadn't caught it because -- damn -- you didn't have "use strict" in gitolite.pm --- src/gitolite.pm | 18 +++++++++++------- src/gl-auth-command | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index a8127e9..a6dba92 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -1,3 +1,4 @@ +use strict; # this file is commonly used using "require". It is not required to use "use" # (because it doesn't live in a different package) @@ -17,16 +18,19 @@ # common definitions # ---------------------------------------------------------------------------- -$ABRT = "\n\t\t***** ABORTING *****\n "; -$WARN = "\n\t\t***** WARNING *****\n "; +our $ABRT = "\n\t\t***** ABORTING *****\n "; +our $WARN = "\n\t\t***** WARNING *****\n "; # commands we're expecting -$R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/; -$W_COMMANDS=qr/^git[ -]receive-pack$/; +our $R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/; +our $W_COMMANDS=qr/^git[ -]receive-pack$/; # note that REPONAME_PATT allows "/", while USERNAME_PATT allows "@" -$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/+-]*$); # very simple pattern -$USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple pattern +our $REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/+-]*$); # very simple pattern +our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple pattern + +our $REPO_UMASK; +our %repos; # ---------------------------------------------------------------------------- # convenience subs @@ -163,7 +167,7 @@ sub report_basic sub special_cmd { - my ($GL_ADMINDIR, $GL_CONF_COMPILED, $RSYNC_BASE, $HTPASSWD_FILE) = @_; + my ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE) = @_; my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; my $user = $ENV{GL_USER}; diff --git a/src/gl-auth-command b/src/gl-auth-command index 8aa2f65..48c67be 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -114,7 +114,7 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/); unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) { # ok, it's not a normal git command; call the special command helper - &special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $RSYNC_BASE, $HTPASSWD_FILE); + &special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE); exit; } From 55a71f00e1fcf055638fac612197ca5c36456db9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 4 Feb 2010 22:55:11 +0530 Subject: [PATCH 239/850] compile: die on authkeys write failure --- src/gl-compile-conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index bb05d1e..eb8f8d7 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -502,5 +502,6 @@ close $newkeys_fh or die "$ABRT close newkeys failed: $!\n"; # all done; overwrite the file (use cat to avoid perm changes) system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); -system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys"); +system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys") + and die "couldn't write authkeys file\n"; system("rm $ENV{HOME}/.ssh/new_authkeys"); From 00b793f5e64ea95a1556f7dae3e5ba6dd17a0f50 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Wed, 3 Feb 2010 19:11:09 +0200 Subject: [PATCH 240/850] Set gitweb.owner config for new wildrepos When creating new wildrepos, add git config to tell gitweb the owner of the repository. Signed-off-by: Teemu Matilainen --- src/gitolite.pm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 045e339..a73f255 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -123,7 +123,10 @@ sub new_repo # erm, note that's "and die" not "or die" as is normal in perl wrap_chdir("$repo.git"); system("git --bare init >&2"); - system("echo $creater > gl-creater") if $creater; + if ($creater) { + system("echo $creater > gl-creater"); + system("git", "config", "gitweb.owner", $creater); + } # propagate our own, plus any local admin-defined, hooks system("cp $hooks_dir/* hooks/"); chmod 0755, "hooks/update"; From fa65d719a83fa33d8f8d95abdf4c9013dc1effc4 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Thu, 4 Feb 2010 23:40:13 +0200 Subject: [PATCH 241/850] Enable setting desription for wildrepos Allow users to set and display description (for gitweb) for their own wildcard repositories using ssh commands: setdesc getdesc Signed-off-by: Teemu Matilainen --- src/gitolite.pm | 20 ++++++++++++++++++++ src/gl-auth-command | 6 +++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index a73f255..eb70dec 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -180,6 +180,26 @@ sub get_set_perms } } +# ---------------------------------------------------------------------------- +# getdesc and setdesc +# ---------------------------------------------------------------------------- + +sub get_set_desc +{ + my($repo_base_abs, $repo, $verb, $user) = @_; + my ($creater, $dummy, $dummy2) = &repo_rights($repo_base_abs, $repo, ""); + die "$repo doesnt exist or is not yours\n" unless $user eq $creater; + wrap_chdir("$repo_base_abs"); + wrap_chdir("$repo.git"); + if ($verb eq 'getdesc') { + system("cat", "description") if -f "description"; + } else { + system("cat > description"); + print "New description is:\n"; + system("cat", "description"); + } +} + # ---------------------------------------------------------------------------- # parse the compiled acl # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index 26fbf71..253ccad 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -86,7 +86,7 @@ my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" # get and set perms for actual repo created by wildcard-autoviv # ---------------------------------------------------------------------------- -my $CUSTOM_COMMANDS=qr/^\s*(expand|getperms|setperms)\s/; +my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\s/; # note that all the subs called here chdir somewhere else and do not come # back; they all blithely take advantage of the fact that processing custom @@ -98,6 +98,10 @@ if ($cmd =~ $CUSTOM_COMMANDS) { # with an actual reponame, you can "getperms" or "setperms" get_set_perms($repo_base_abs, $repo, $verb, $user); } + elsif ($repo =~ $REPONAME_PATT and $verb =~ /(get|set)desc/) { + # with an actual reponame, you can "getdesc" or "setdesc" + get_set_desc($repo_base_abs, $repo, $verb, $user); + } elsif ($verb eq 'expand') { # with a wildcard, you can "expand" it to see what repos actually match die "$repo has invalid characters" unless "x$repo" =~ $REPOPATT_PATT; From 85cc31c77134d78d3ff3ca2fd2229d0e5b791530 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 5 Feb 2010 06:49:07 +0530 Subject: [PATCH 242/850] install/pm: turn hooks from copies to symlinks --- src/gitolite.pm | 14 +++++++++++++- src/gl-install | 6 ++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index a6dba92..9f2193f 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -78,6 +78,18 @@ sub check_ref { die "$perm $ref $repo $ENV{GL_USER} DENIED by fallthru\n"; } +# ln -sf :-) +sub ln_sf +{ + my($srcdir, $glob, $dstdir) = @_; + for my $hook ( glob("$srcdir/$glob") ) { + $hook =~ s/$srcdir\///; + unlink "$dstdir/$hook"; + symlink "$srcdir/$hook", "$dstdir/$hook" or die "could not symlink $hook\n"; + } +} + + # ---------------------------------------------------------------------------- # where is the rc file hiding? # ---------------------------------------------------------------------------- @@ -124,7 +136,7 @@ sub new_repo wrap_chdir("$repo.git"); system("git --bare init >&2"); # propagate our own, plus any local admin-defined, hooks - system("cp $hooks_dir/* hooks/"); + ln_sf($hooks_dir, "*", "hooks"); chmod 0755, "hooks/update"; } diff --git a/src/gl-install b/src/gl-install index 9494fc6..9159ed5 100755 --- a/src/gl-install +++ b/src/gl-install @@ -71,14 +71,16 @@ chdir("$repo_base_abs") or die "chdir $repo_base_abs failed: $!\n"; for my $repo (`find . -type d -name "*.git"`) { chomp ($repo); # propagate our own, plus any local admin-defined, hooks - system("cp $GL_ADMINDIR/src/hooks/* $repo/hooks/"); + ln_sf("$GL_ADMINDIR/src/hooks", "*", "$repo/hooks"); chmod 0755, "$repo/hooks/update"; } # oh and one of those repos is a bit more special and has an extra hook :) if ( -d "gitolite-admin.git/hooks" ) { print "copying post-update hook to gitolite-admin repo...\n"; - system("cp $GL_ADMINDIR/src/ga-post-update-hook gitolite-admin.git/hooks/post-update"); + unlink "gitolite-admin.git/hooks/post-update"; + symlink "$GL_ADMINDIR/src/ga-post-update-hook", "gitolite-admin.git/hooks/post-update" + or die "could not symlink post-update hook\n"; chmod 0755, "gitolite-admin.git/hooks/post-update"; } From 388f4d873d9844f771589e8c7aec2a862d84f1df Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 5 Feb 2010 16:00:47 +0530 Subject: [PATCH 243/850] (IMPORTANT; read this in full) no more "wildrepos" The wildrepos branch has been merged into master, and deleted. It will no longer exist as a separate branch. Instead, a new variable called $GL_WILDREPOS has been added which acts as a switch; when off (which is the default), many wildrepos features are disabled. (the "C" permissions, and the getperms (etc.) commands mainly). Important: if you are using wildrepos, please set "$GL_WILDREPOS = 1;" in the RC file when you upgrade to this version (or just before you do the upgrade). --- README.mkd | 13 +++++------ conf/example.gitolite.rc | 40 ++++++++++++++++++++++++++++++++- doc/3-faq-tips-etc.mkd | 5 ++--- doc/4-wildcard-repositories.mkd | 33 +++++++++++++++++---------- src/gitolite.pm | 8 ++++++- src/gl-auth-command | 7 +++--- src/gl-compile-conf | 6 +++-- 7 files changed, 82 insertions(+), 30 deletions(-) diff --git a/README.mkd b/README.mkd index 7f3956a..12596a4 100644 --- a/README.mkd +++ b/README.mkd @@ -86,10 +86,7 @@ detail [here][gsdiff]. ### security Due to the environment in which this was created and the need it fills, I -consider this a "security" program, albeit a very modest one. The code is -very small and easily reviewable -- the 2 programs that actually control -access when a user logs in total about 220 lines of code (about 90 lines -according to "sloccount"). +consider this a "security" program, albeit a very modest one. For the first person to find a security hole in it, defined as allowing a normal user (not the gitolite admin) to read a repo, or write/rewind a ref, @@ -99,10 +96,10 @@ or in Unix, perl, shell, etc.)... well I can't afford 1000 USD rewards like djb, so you'll have to settle for 1000 INR (Indian Rupees) as a "token" prize :-) -Update 2010-01-31: this security promise does not apply if you enable any of -the external command helpers (like rsync). It's probably quite secure, but I -just haven't thought about it enough to be able to make such promises, like I -can for the rest of "master". +However, there are a few optional features (which must be explicitly enabled +in the RC file) where I just haven't had the time to reason about security +thoroughly enough. Please read the comments in `conf/example.gitolite.rc` for +details, looking for the word "security". ---- diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index bcb281f..ddf5baa 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -108,6 +108,26 @@ $GIT_PATH=""; # -------------------------------------- + + +# ---------------------------------------------------------------------- +# SECURITY SENSITIVE SETTINGS +# +# Settings below this point may have security implications. That +# usually means that I have not thought hard enough about all the +# possible ways to crack security if these settings are enabled. + +# Please see details on each setting for specifics, if any. +# ---------------------------------------------------------------------- + + + +# -------------------------------------- +# EXTERNAL COMMAND HELPER -- HTPASSWD + +# security note: runs an external command (htpasswd) with specific arguments, +# including a user-chosen "password". + # if you want to enable the "htpasswd" command, give this the absolute path to # whatever file apache (etc) expect to find the passwords in. @@ -118,7 +138,10 @@ $HTPASSWD_FILE = ""; # -------------------------------------- # EXTERNAL COMMAND HELPER -- RSYNC -# + +# security note: runs an external command (rsync) with specific arguments, all +# presumably filled in correctly by the client-side rsync. + # base path of all the files that are accessible via rsync. Must be an # absolute path. Leave it undefined or set to the empty string to disable the # rsync helper. @@ -126,6 +149,21 @@ $RSYNC_BASE = ""; # $RSYNC_BASE = "/home/git/up-down"; # $RSYNC_BASE = "/tmp/up-down"; +# -------------------------------------- +# ALLOW REPO CONFIG TO USE WILDCARDS + +# security note: this used to in a separate "wildrepos" branch. You can +# create repositories based on wild cards, give "ownership" to the specific +# user who created it, allow him/her to hand out R and RW permissions to other +# users to collaborate, etc. This is powerful stuff, and I've made it as +# secure as I can, but it hasn't had the kind of rigorous line-by-line +# analysis that the old "master" branch had. + +# This has now been rolled into master, with all the functionality gated by +# this variable. Set this to 1 if you want to enable the wildrepos features. +# Please see doc/4-wildcard-repositories.mkd for details. +# $GL_WILDREPOS = 0; + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index b2a1ba7..2cf7381 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -149,7 +149,7 @@ plain "git archive", because the Makefile adds a file called git clone git://sitaramc.indefero.net/sitaramc/gitolite.git cd gitolite make master.tar - # or maybe "make wildrepos.tar" or "make pu.tar" + # or maybe "make pu.tar" @@ -626,8 +626,7 @@ per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and #### repos named with wildcards -**This feature only exists in the "wildrepos" branch!** Please see -`doc/4-wildcard-repositories.mkd` for all the details. +Please see `doc/4-wildcard-repositories.mkd` for all the details. #### access control for external commands diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 2324a02..aba4f66 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -2,15 +2,14 @@ ***IMPORTANT NOTE***: -This branch contains features that are likely to be much more brittle than the -"master" branch. Creating repositories based on wild cards, giving -"ownership" to the specific user who created it, allowing him/her to hand out -R and RW permissions to other users to collaborate, all these are possible. -And any of these could have a bug in it. +This feature may be somewhat "brittle" in terms of security. Creating +repositories based on wild cards, giving "ownership" to the specific user who +created it, allowing him/her to hand out R and RW permissions to other users +to collaborate, all these are possible. And any of these could have a bug in +it. I haven't found any yet, but that doesn't mean there aren't any. -"Brittle" also means some features in "master" may not work here. For -example, you cannot specify gitconfig values for a wildcard repo; it only -works for actual repos. +Also, there are some limitations. For example, you cannot specify gitconfig +values for a wildcard repo; it only works for actual repos. There may be other such missing features. Sometimes it's just not possible to make it work. Or it may be cumbersome enough that unless there are *no* @@ -25,7 +24,8 @@ In this document: * wildcard repos without creater name in them * side-note: line-anchored regexes * contrast with refexes - * handing out rights to wildcard-matached repos + * handing out rights to wildcard-matched repos + * setting a gitweb description for a wildcard-matched repo * reporting * other issues and discussion @@ -121,7 +121,7 @@ actually push such a branch! You can anchor it if you really care, by using `master$` instead of `master`, but anchoring is *not* the default for refexes.] -### Handing out rights to wildcard-matached repos +### Handing out rights to wildcard-matched repos In the examples above, we saw two special "user" names: READERS and WRITERS. The permissions they have are controlled by the config file, but ***who is @@ -166,7 +166,12 @@ The following points are important: * whoever you specify as "R" will match the special user READERS. "RW" will match WRITERS. -### Reporting +### setting a gitweb description for a wildcard-matched repo + +Similar to the getperm/setperm commands, there are the getdesc/setdesc +commands, thanks to Teemu. + +### reporting Remember the cool stuff you see when you just do `ssh git@server` (grep for "myrights" in `doc/3-faq-tips-etc.mkd` if you forgot, or go [here][mr]). @@ -177,7 +182,11 @@ This still works, except the format is a little more compressed to accommodate a new column (at the start) for "C" permissions, which indicate that you are allowed to *create* repos matching that pattern. -### Other issues and discussion +In addition, there is also the "expand" command, which takes any regex pattern +and returns you a list of all wildcard-created repos that you have access to +which fit that pattern. + +### other issues and discussion * *what if the repo name being pushed matches more than one pattern*? diff --git a/src/gitolite.pm b/src/gitolite.pm index 106624a..7f85315 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -33,7 +33,8 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple patter # same as REPONAME, plus some common regex metas our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); -our $REPO_UMASK; +# these come from the RC file +our ($REPO_UMASK, $GL_WILDREPOS); our %repos; # ---------------------------------------------------------------------------- @@ -134,6 +135,8 @@ sub new_repo my ($repo, $hooks_dir, $creater) = @_; umask($REPO_UMASK); + die "wildrepos disabled, can't set creater $creater on new repo $repo\n" + if $creater and not $GL_WILDREPOS; system("mkdir", "-p", "$repo.git") and die "$ABRT mkdir $repo.git failed: $!\n"; # erm, note that's "and die" not "or die" as is normal in perl @@ -226,6 +229,7 @@ sub parse_acl # please update that also if the interface or the env vars change my ($GL_CONF_COMPILED, $repo, $c, $r, $w) = @_; + $c = $r = $w = "NOBODY" unless $GL_WILDREPOS; # void $r if same as $w (otherwise "readers" overrides "writers"; this is # the same problem that needed a sort sub for the Dumper in the compile @@ -251,6 +255,8 @@ sub parse_acl return unless $repo; return $ENV{GL_REPOPATT} = "" if $repos{$repo}; + # didn't find it, but wild is off? too bad, die!!! muahahaha + die "$repo not found in compiled config\n" unless $GL_WILDREPOS; # didn't find $repo in %repos, so it must be a wildcard-match case my @matched = grep { $repo =~ /^$_$/ } sort keys %repos; diff --git a/src/gl-auth-command b/src/gl-auth-command index 3628c19..f7f415a 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -24,7 +24,7 @@ use warnings; # ---------------------------------------------------------------------------- # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; @@ -110,6 +110,7 @@ my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\s/; # commands is sort of a dead end for normal (git) processing if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) { + die "wildrepos disabled, sorry\n" unless $GL_WILDREPOS; my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; my ($verb, $repo) = ($cmd =~ /^\s*(\S+)\s+\/?(.*?)(?:.git)?$/); if ($repo =~ $REPONAME_PATT and $verb =~ /getperms|setperms/) { @@ -171,8 +172,8 @@ if ( -d "$repo_base_abs/$repo.git" ) { } else { &parse_acl($GL_CONF_COMPILED, $repo, $user, $user, $user); - # auto-vivify new repo if you have C access - if ( $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'} ) { + # auto-vivify new repo if you have C access (and wildrepos is on) + if ( $GL_WILDREPOS and $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'} ) { wrap_chdir("$repo_base_abs"); new_repo($repo, "$GL_ADMINDIR/src/hooks", $user); wrap_chdir($ENV{HOME}); diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 5c19ebb..e173a56 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -61,7 +61,7 @@ $Data::Dumper::Sortkeys = sub { return [ reverse sort keys %{$_[0]} ]; }; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $SHELL_USERS); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $SHELL_USERS, $GL_WILDREPOS); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -133,7 +133,8 @@ sub expand_list for my $item (@list) { - die "$ABRT bad user or repo name $item\n" unless $item =~ $REPOPATT_PATT or $item =~ $USERNAME_PATT; + die "$ABRT bad user or repo name $item\n" + unless ($GL_WILDREPOS ? $item =~ $REPOPATT_PATT : $item =~ $REPONAME_PATT) or $item =~ $USERNAME_PATT; if ($item =~ /^@/) # nested group { die "$ABRT undefined group $item\n" unless $groups{$item}; @@ -206,6 +207,7 @@ sub parse_conf_file my $perms = $1; my @refs; @refs = split(' ', $2) if $2; my @users = split ' ', $3; + die "wildrepos disabled, cant use 'C' in config\n" if $perms eq 'C' and not $GL_WILDREPOS; # if no ref is given, this PERM applies to all refs @refs = qw(refs/.*) unless @refs; From b299ff09c37146a597c4b0af9eae5f2fe19bdbc0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 6 Feb 2010 05:43:16 +0530 Subject: [PATCH 244/850] rsync: restrict the "path" part of the received command Although I have washed my hands off the security aspect if you use external commands, that doesn't mean I won't make them as tight as I can ;-) Right now, this is just a place holder -- if people use it and complain that the pattern is too restrictive, I'll change it. --- src/gitolite.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gitolite.pm b/src/gitolite.pm index 7f85315..0f84272 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -411,6 +411,8 @@ sub ext_cmd_rsync my $perm = "W"; $perm = "R" if $1; my $path = $2; + die "I dont like some of the characters in $path\n" unless $path =~ $REPOPATT_PATT; + # XXX make a better pattern for this if people complain ;-) die "I dont like absolute paths in $cmd\n" if $path =~ /^\//; die "I dont like '..' paths in $cmd\n" if $path =~ /\.\./; From a472bf30df3e87991eb92002ffef5386db0620a3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 7 Feb 2010 13:09:16 +0530 Subject: [PATCH 245/850] compile: tighten up the 'git config' feature Gitolite allows you to set git repo options using the "config" keyword; see conf/example.conf for details and syntax. However, if you are in an installation where the repo admin does not (and should not) have shell access to the server, then allowing him to set arbitrary repo config options *may* be a security risk -- some config settings may allow executing arbitrary commands. This patch fixes it, introducing a new RC variable to control the behaviour. See conf/example.gitolite.rc for details --- conf/example.conf | 4 ++++ conf/example.gitolite.rc | 26 ++++++++++++++++++++++++++ src/gl-compile-conf | 6 ++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index a17161e..01ea6d4 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -236,6 +236,10 @@ gitolite "Sitaram Chamarty" = "fast, secure, access control for git in a corpora # REPO SPECIFIC GITCONFIG # ----------------------- +# update 2010-02-06; this won't work unless the rc file has the right +# settings; please see comments around the variable $GL_GITCONFIG_KEYS in +# conf/example.gitolite.rc for details and security information. + # (Thanks to teemu dot matilainen at iki dot fi) # this should be specified within a "repo" stanza diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index ddf5baa..00e6a57 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -122,6 +122,32 @@ $GIT_PATH=""; +# -------------------------------------- +# ALLOW REPO ADMIN TO SET GITCONFIG KEYS +# +# Gitolite allows you to set git repo options using the "config" keyword; see +# conf/example.conf for details and syntax. +# +# However, if you are in an installation where the repo admin does not (and +# should not) have shell access to the server, then allowing him to set +# arbitrary repo config options *may* be a security risk -- some config +# settings may allow executing arbitrary commands. +# +# You have 3 choices. By default $GL_GITCONFIG_KEYS is left undefined, which +# completely disables this feature (meaning you cannot set git configs from +# the repo config). +# +# The second choice is to give it a space separated list of settings you +# consider safe. (These are actually treated as a set of regular expression +# patterns, and any one of them must match). For example: +# $GL_GITCONFIG_KEYS = "core\.logAllRefUpdates core\..*compression"; +# allows repo admins to set one of those 3 config keys (yes, that second +# pattern matches two settings from "man git-config", if you look) +# +# The third choice (which you may have guessed already if you're familiar with +# regular expressions) is to allow anything and everything: +# $GL_GITCONFIG_KEYS = ".*"; + # -------------------------------------- # EXTERNAL COMMAND HELPER -- HTPASSWD diff --git a/src/gl-compile-conf b/src/gl-compile-conf index e173a56..6a02107 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -61,7 +61,7 @@ $Data::Dumper::Sortkeys = sub { return [ reverse sort keys %{$_[0]} ]; }; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $SHELL_USERS, $GL_WILDREPOS); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $SHELL_USERS, $GL_WILDREPOS, $GL_GITCONFIG_KEYS); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -288,7 +288,9 @@ sub parse_conf_file elsif (/^config (.+) = ?(.*)/) { my ($key, $value) = ($1, $2); - die "$WARN $fragment attempting to set repo configuration\n" if $fragment ne 'master'; + my @validkeys = split (' ', ($GL_GITCONFIG_KEYS || '')); + my @matched = grep { $key =~ /^$_$/ } @validkeys; + die "$ABRT git config $key not allowed\n" if (@matched < 1); for my $repo (@repos) # each repo in the current stanza { $repo_config{$repo}{$key} = $value; From 1f9fbfa71eaacec179d6d2694384531568adfdd0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 7 Feb 2010 19:10:53 +0530 Subject: [PATCH 246/850] get "info" for users other than yourself if you have read access to the admin repo, you can say ssh git@server info user1 [...] Original idea and code by Karteek E. The motivation is to quickly and easily check what perms a user has. Technically nothing that you can't glean from the config file itself but it serves as a double check or a mild debugging aid perhaps. However note that the branch level rules are much more complex and they do not, as yet, have any such "helpful" aids. Life is like that sometimes. --- src/gitolite.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gitolite.pm b/src/gitolite.pm index 0f84272..5638d05 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -352,6 +352,14 @@ sub special_cmd if ($cmd eq 'info') { &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); print "you also have shell access\n\r" if $shell_allowed; + } elsif ($cmd =~ /^info\s+(.+)$/) { + my @otherusers = split ' ', $1; + &parse_acl($GL_CONF_COMPILED); + die "you can't ask for others' permissions\n" unless $repos{'gitolite-admin'}{'R'}{$user}; + for my $otheruser (@otherusers) { + warn("ignoring illegal username $otheruser\n"), next unless $otheruser =~ $USERNAME_PATT; + &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $otheruser); + } } elsif ($HTPASSWD_FILE and $cmd eq 'htpasswd') { &ext_cmd_htpasswd($HTPASSWD_FILE); } elsif ($RSYNC_BASE and $cmd =~ /^rsync /) { From a6b7928bc140a9f08b14af4865595440ad20cd6c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 8 Feb 2010 06:02:36 +0530 Subject: [PATCH 247/850] example RC: GL_GITCONFIG_KEYS was not showing up in easy install diff --- conf/example.gitolite.rc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 00e6a57..f1e5b7e 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -133,9 +133,10 @@ $GIT_PATH=""; # arbitrary repo config options *may* be a security risk -- some config # settings may allow executing arbitrary commands. # -# You have 3 choices. By default $GL_GITCONFIG_KEYS is left undefined, which +# You have 3 choices. By default $GL_GITCONFIG_KEYS is left empty, which # completely disables this feature (meaning you cannot set git configs from # the repo config). +$GL_GITCONFIG_KEYS = ""; # # The second choice is to give it a space separated list of settings you # consider safe. (These are actually treated as a set of regular expression @@ -188,7 +189,7 @@ $RSYNC_BASE = ""; # This has now been rolled into master, with all the functionality gated by # this variable. Set this to 1 if you want to enable the wildrepos features. # Please see doc/4-wildcard-repositories.mkd for details. -# $GL_WILDREPOS = 0; +$GL_WILDREPOS = 0; # -------------------------------------- # per perl rules, this should be the last line in such a file: From 0e96c2f08a5f099ac4df668fa460baba1c7cb05a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 8 Feb 2010 09:59:27 +0530 Subject: [PATCH 248/850] example conf: doc on NAME/ being a refex etc was not clear --- conf/example.conf | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 01ea6d4..e6aac3b 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -83,6 +83,7 @@ # If none of them match, it fails # what's a refex? a regex to match against the ref being updated (get it?) +# See next section for more on refexes # BASIC PERMISSIONS (repo level only; apply to all branches/tags in repo) @@ -114,13 +115,16 @@ repo @oss_repos repo @all RW+ = @admins -# ADVANCED PERMISSIONS USING REFEXES +# SPECIFYING AND USING A REFEX # - refexes are specified in perl regex syntax -# - refexes are matched without any anchoring, which means a refex like -# "refs/tags/v[0-9]" matches anything *containing* that pattern. There -# may be text before and after it (example: refs/tags/v4-r3p7), and it -# will still match +# - refexes are prefix-matched (they are internally anchored with "^" +# before being used), which means a refex like "refs/tags/v[0-9]" +# matches anything *starting with* that pattern. There may be text +# after it (example: refs/tags/v4-r3/p7), and it will still match + +# ADVANCED PERMISSIONS USING REFEXES + # - if no refex appears, the rule applies to all refs in that repo # - a refex is automatically prefixed by "refs/heads/" if it doesn't start # with "refs/" (so tags have to be explicitly named as @@ -191,7 +195,9 @@ repo foo # Notes -# - the "NAME/" is part of the syntax; think of it as a keyword if you like +# - the "NAME/" is part of the syntax; think of it as a keyword if you like. +# The rest of it is treated as a refex to match against each file being +# touched (see "SPECIFYING AND USING A REFEX" above for details) # - file/dir NAME-based restrictions are *in addition* to normal (branch-name # based) restrictions; they are not a *replacement* for them. This is why @@ -284,7 +290,7 @@ repo gitolite repo EXTCMD/rsync # grant permissions to files/dirs within the $RSYNC_BASE tree. A leading # NAME/ is required as a prefix; the actual path starts after that. Matching -# follows the same rules as elsewhere in gitolite. +# follows the same rules as given in "FILE/DIR NAME BASED RESTRICTIONS" above RW NAME/ = sitaram RW NAME/foo/ = user1 R NAME/bar/ = user2 From 72bac2a21a53c2c85fe69008a93e87ccd832810f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 8 Feb 2010 16:38:28 +0530 Subject: [PATCH 249/850] dps: (distro packaging support) dont let install copy the sample conf --- src/gl-install | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/gl-install b/src/gl-install index 9159ed5..8a59569 100755 --- a/src/gl-install +++ b/src/gl-install @@ -56,13 +56,14 @@ for my $dir qw(conf doc keydir logs src) { system("cp -R src doc $GL_ADMINDIR"); unless (-f $GL_CONF) { - system("cp conf/example.conf $GL_CONF"); print < Date: Thu, 11 Feb 2010 09:03:26 +0530 Subject: [PATCH 250/850] doc/3 reorg; one section was getting too long! --- doc/3-faq-tips-etc.mkd | 561 +++++++++++++++++++++-------------------- 1 file changed, 290 insertions(+), 271 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 2cf7381..784f58b 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -10,28 +10,33 @@ In this document: * `@all` syntax for repos * umask setting * getting a tar file from a clone - * differences from gitosis - * simpler syntax - * two levels of access rights checking - * file/dir NAME based restrictions - * error checking the config file - * including config lines from other files - * delegating parts of the config file - * easier to specify gitweb "description" and gitweb/daemon access - * easier to link gitweb authorisation with gitolite - * better logging - * one user, many keys - * support for git installed outside default PATH - * what repos do I have access to? - * "exclude" (or "deny") rules - * "personal" branches - * custom hooks and custom git config - * repos named with wildcards - * access control for external commands + * features + * syntax and normal usage + * simpler syntax + * one user, many keys + * security, access control, and auditing + * two levels of access rights checking + * better logging + * "exclude" (or "deny") rules + * file/dir NAME based restrictions + * delegating parts of the config file + * convenience features + * what repos do I have access to? + * error checking the config file + * including config lines from other files + * support for git installed outside default PATH + * "personal" branches + * custom hooks and custom git config + * helping with gitweb + * easier to specify gitweb "description" and gitweb/daemon access + * easier to link gitweb authorisation with gitolite + * advanced features + * repos named with wildcards + * access control for external commands * design choices * keeping the parser and the access control separate -### common errors and mistakes +## common errors and mistakes * adding `repositories/` at the start of the repo name in the `git clone`. This error is typically made by the *admin* himself -- because he knows @@ -53,7 +58,7 @@ In this document: Please see doc/6-ssh-troubleshooting.mkd for what all this means. -### git version dependency +## git version dependency Here's a workaround for a version dependency that the normal flow of gitolite has. @@ -82,9 +87,9 @@ and then push. Something like: Once this is done, the repo is available for cloning by anyone else in the normal way, since it's not empty anymore. -### other errors, warnings, notes... +## other errors, warnings, notes... -#### ssh-copy-id +### ssh-copy-id don't have `ssh-copy-id`? This is broadly what that command does, if you want to replicate it manually. The input is your pubkey, typically @@ -107,7 +112,7 @@ typically) also must be `go-w`, but that needs root. And typically they're already set that way anyway. (Or if they're not, you've got bigger problems than gitolite install not working!)] -#### cloning an empty repo +### cloning an empty repo Cloning an empty repo is only possible with clients greater than 1.6.2. So at least one of your clients needs to have a recent git. Once at least one @@ -118,7 +123,7 @@ end hung up unexpectedly`. However, you can ignore this, since it doesn't seem to hurt anything. [Update 2009-09-14; this has been fixed in git 1.6.4.3] -#### `@all` syntax for repos +### `@all` syntax for repos There *is* a way to use the `@all` syntax for repos also, as described in `conf/example.conf`. However, there is an important difference between this @@ -132,12 +137,12 @@ and the old `@all` (for users): * This means that if you really want *all* repos, you'd better put this para at the **end** of the config file! -#### umask setting +### umask setting Gitweb not able to read your repos? You can change the umask for newly created repos to something more relaxed -- see the `~/.gitolite.rc` file -### getting a tar file from a clone +## getting a tar file from a clone You can clone the repo from github or indefero, then execute a make command to extract a tar file of the branch you want. Please use the make command, not a @@ -153,13 +158,9 @@ plain "git archive", because the Makefile adds a file called -### differences from gitosis +## features -Apart from the big ones listed in the top level README, and subjective ones -like "better config file format", there are some small, but significant and -concrete, differences from gitosis. - - +### syntax and normal usage #### simpler syntax @@ -204,6 +205,51 @@ do not worry that this causes some duplication or inefficiency. It doesn't See the "specify gitweb/daemon access" section below for one more example. +#### one user, many keys + +I have a laptop and a desktop I need to access the server from. I have +different private keys on them, but as far as gitolite is concerned both of +them should be treated as "sitaram". How does this work? + +In gitosis, the admin creates a single "sitaram.pub" containing one line for +each of my pubkeys. In gitolite, we keep them separate: "sitaram@laptop.pub" +and "sitaram@desktop.pub". The part before the "@" is the username, so +gitolite knows these two keys belong to the same person. + +Note that you don't say "sitaram@laptop" and so on in the **config** file -- +as far as the config file is concerned there's just **one** user called +"sitaram" -- so you only say "sitaram" there. + +I think this is easier to maintain if you have to delete or change one of +those keys. + +However, now that `sitaramc@gmail.com` is also a valid username, we need to +distinguish between `sitaramc@gmail.com.pub` and `sitaramc@desktop.pub`. We +do that by requiring that the multi-key suffix you use (like "desktop" and +"laptop") should not have a `"."` in it. If it does, it looks like an email +address. The following table lists sample pubkey filenames and the +corresponding derived usernames (which is what goes into the +`conf/gitolite.conf` file): + + * old style multikeys; not mistaken for emails because there is no "." in + hostname part + + sitaramc.pub sitaramc + sitaramc@laptop.pub sitaramc + sitaramc@desktop.pub sitaramc + + * new style, email keys; there is a "." in hostname part; so it's an email + address + + sitaramc@gmail.com.pub sitaramc@gmail.com + + * multikeys *with* email address + + sitaramc@gmail.com@laptop.pub sitaramc@gmail.com + sitaramc@gmail.com@desktop.pub sitaramc@gmail.com + +### security, access control, and auditing + #### two levels of access rights checking Gitolite has two levels of access checks. The **first check** is what I will @@ -239,6 +285,89 @@ any of the refexes match, the push succeeds. If none of them match, it fails. Gitolite also allows "exclude" or "deny" rules. See later in this document for details. +Apart from the big ones listed in the top level README, and subjective ones +like "better config file format", gitolite has evolved to have many useful +fearures than the original goal of "gitosis + branch-level access control". + + + +#### better logging + +If you have been too liberal with the permission to rewind, it has built-in +logging as an emergency fallback if someone goes too far, or for audit +purposes [`*`]. The logfile names and location are configurable, and can +include the year/month/day etc in the filename for easy archival or further +processing. The log file even tells you which pattern in the config file +matched to allow that specific access to proceed. + +> [`*`] setting `core.logAllRefUpdates true` does provide a safety net +> against over-zealous rewinds, but it does not tell you "who". And +> strangely, management does not seem to share the view that "blame" is just +> a synonym for "annotate" ;-)] + +The log lines look like this: + + 2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master + +The "+" at the start indicates a non-fast forward update, in this case from +b4e76569659939 to 4fb16f2a88d8b5. So b4e76569659939 is the one to restore! +Can it get easier? + +The other parts of the log line are the name of the repo, the refname being +updated, the user updating it, and the refex pattern (from the config file) +that matched, in case you need to debug the config file itself. + + + +#### "exclude" (or "deny") rules + +Here is an illustrative explanation of "deny" rules. However, please be sure +to read the "DENY/EXCLUDE RULES" section in `conf/example.conf` for important +notes/caveats before using "deny" rules. + +Take a look at the following snippet, which *seems* to say that "bruce" can +write versioned tags (anything containing `refs/tags/v[0-9]`), but the other +staffers can't: + + @staff = bruce whitfield martin + [... and later ...] + RW refs/tags/v[0-9] = bruce + RW refs/tags = @staff + +But that's not how the matching works. As long as any refex matches the +refname being updated, it's a "yes". Since the second refex (which says +"anything containing `refs/tags`") is a superset of the first one, it lets +anyone on `@staff` create versioned tags, not just Bruce. + +One way to fix this is to allow "excludes" -- some changes in syntax, combined +with a rigorous, ordered, interpretation would do it. + +Let's recap the **existing semantics**: + +> the first matching refex that has the permission you're looking for (`W` +> or `+`), results in success. A fallthrough results in failure + +Here are the **new semantics**, with changes from the "main" one in bold: + +> the first matching refex that has the permission you're looking for (`W` +> or `+`) **or a minus (`-`)**, results in success **or failure, +> respectively**. A fallthrough **also** results in failure + +So the example we started with becomes, if you use "deny" rules: + + RW refs/tags/v[0-9] = bruce + - refs/tags/v[0-9] = @staff + RW refs/tags = @staff + +And here's how it works: + + * for non-version tags, only the 3rd rule matches, so anyone on staff can + push them + * for version tags by bruce, the first rule matches so he can push them + * for version tags by staffers *other than bruce*, the second rule matches + before the third one, and it has a `-` as the permission, so the push + fails + #### file/dir NAME based restrictions In addition to branch-name based restrictions, gitolite also allows you to @@ -248,6 +377,46 @@ changed, treating each filename as a "ref" to be matched. Please see `conf/example.conf` for syntax and examples. +#### delegating parts of the config file + +You can now split up the config file and delegate the authority to specify +access control for their own pieces. See +[doc/5-delegation.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/5-delegation.mkd) +for details. + + + +### convenience features + +#### what repos do I have access to? + +Sometimes there are too many repos, maybe even named similarly, or with the +potential for typos, confusion about hyphens/underscores or upper/lower case, +etc. You'd just like a simple way to know what repos you have access to. + +Easy! Just use ssh and try to log in as if you were attempting to get a +shell: + + $ ssh gitolite info + PTY allocation request failed on channel 0 + hello sitaram, the gitolite version here is v0.6-17-g94ed189 + you have the following permissions: + R W Anu-WSD + R ROtest + R W SecureBrowse + R W entrans + R W git-notes + R W gitolite + R W gitolite-admin + R W indic_web_input + R W proxy + @ @ testing + R W vkc + +Note that until this version, we used to put out an ugly `need +SSH_ORIGINAL_COMMAND` error, just like gitosis used to. All we did is put +that code path to better use :-) + #### error checking the config file gitosis does not do any. I just found out that if you mis-spell `members` as @@ -261,14 +430,94 @@ you know right away. See the entry under "INCLUDE SOME OTHER FILE" in `conf/example.conf`. -#### delegating parts of the config file +#### support for git installed outside default PATH -You can now split up the config file and delegate the authority to specify -access control for their own pieces. See -[doc/5-delegation.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/5-delegation.mkd) -for details. +The normal solution is to add to the system default PATH somehow, either by +munging `/etc/profile` or by enabling `PermitUserEnvironment` in +`/etc/ssh/sshd_config` and then setting the PATH in `~/.ssh/.environment`. +All these are security risks because they allow a lot more than just you and +your git install :-) - +And if you don't have root, you can't do this anyway. + +The only solution till now has been to ask every client to set the config +parameters `remote..receivepack` and `remote..uploadpack`. But +telling *every* client to do so is a pain... + +Gitolite lets you specify the directory in which git binaries are to be found, +via a new variable (`$GIT_PATH`) in the "rc" file. If this variable is +non-empty, it will be appended to the PATH environment variable before +attempting to run git stuff. + +Very easy, very simple, and completely transparent to the users :-) + + + +#### "personal" branches + +"personal" branches are great for corporate environments, where +unauthenticated pull/clone is a no-no. Since a dev workstation cannot do +authentication, even work shared just between 2 devs has to go *via* the +server. This causes the same branch name clutter as in a centralised VCS, +plus setting up permissions for this becomes a chore for the admin. + +gitolite lets you define a "personal" or "scratch" namespace prefix for +each developer (e.g., `refs/personal//*`), with full +permissions for that dev and read-only for everyone else. And you get +this without adding a single line to the access config file -- pretty +much fire and forget as far as the admin is concerned, even if there is +constant churn in the project teams. + +Not bad for something that took just *one* line of code to implement. +And that's one clean, readable, line, by the way ;-) + +The admin would set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate +this to all users. It could be something like `refs/heads/personal`, which +means all such branches will show up in `git branch` lookups and `git clone` +will fetch them. Or he could use, say, `refs/personal`, which means it won't +show up in any normal "branch-y" commands and stuff, and generally be much +less noisy. + +**Note that a user who has NO write access cannot have personal branches**; if +you read the section (above) on "two levels of access rights checking" you'll +understand why. + +For instance, in the following example, `user3` cannot push to any +`refs/heads/personal/user3/*` branches because the first level check stops him +cold: + + # assume $PERSONAL = 'refs/heads/personal' in ~/.gitolite.rc + repo myrepo + RW+ master = sitaram + RW+ release = qa_guy + RW = user1 user2 + R = user3 + +If we relax that check, *any* access becomes *write* access. Yes it will be +caught later, by the hook, but it's good practice to catch things in multiple +places. + +If you want `user3` to have his own personal branch, but without write access +to any of the "real" branches (like "master", "release", etc.), just use a +dummy branch. Choose a name that will never exist in practice, or even if +someone creates it, we don't care. For example, this will get him past the +first check: + + RW dummy = user3 + +Just don't *show* the user this config file; it might sound insulting :-) + +#### custom hooks and custom git config + +You can specify hooks that you want to propagate to all repos, as well as +per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and +`conf/example.conf` for details. + +### helping with gitweb + +Although gitweb is a completely separate program, gitolite can do quite a +lot to help you manage gitweb access as well; once the initial setup is +complete, you can do it all from within the gitolite config file! #### easier to specify gitweb "description" and gitweb/daemon access @@ -392,237 +641,7 @@ already done and we just use it! [leho]: http://leho.kraav.com/news/2009/10/27/using-apache-authentication-with-gitweb-gitosis-repository-access-control/ -#### better logging - -If you have been too liberal with the permission to rewind, it has built-in -logging as an emergency fallback if someone goes too far, or for audit -purposes [`*`]. The logfile names and location are configurable, and can -include the year/month/day etc in the filename for easy archival or further -processing. The log file even tells you which pattern in the config file -matched to allow that specific access to proceed. - -> [`*`] setting `core.logAllRefUpdates true` does provide a safety net -> against over-zealous rewinds, but it does not tell you "who". And -> strangely, management does not seem to share the view that "blame" is just -> a synonym for "annotate" ;-)] - -The log lines look like this: - - 2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master - -The "+" at the start indicates a non-fast forward update, in this case from -b4e76569659939 to 4fb16f2a88d8b5. So b4e76569659939 is the one to restore! -Can it get easier? - -The other parts of the log line are the name of the repo, the refname being -updated, the user updating it, and the refex pattern (from the config file) -that matched, in case you need to debug the config file itself. - - - -#### one user, many keys - -I have a laptop and a desktop I need to access the server from. I have -different private keys on them, but as far as gitolite is concerned both of -them should be treated as "sitaram". How does this work? - -In gitosis, the admin creates a single "sitaram.pub" containing one line for -each of my pubkeys. In gitolite, we keep them separate: "sitaram@laptop.pub" -and "sitaram@desktop.pub". The part before the "@" is the username, so -gitolite knows these two keys belong to the same person. - -Note that you don't say "sitaram@laptop" and so on in the **config** file -- -as far as the config file is concerned there's just **one** user called -"sitaram" -- so you only say "sitaram" there. - -I think this is easier to maintain if you have to delete or change one of -those keys. - -However, now that `sitaramc@gmail.com` is also a valid username, we need to -distinguish between `sitaramc@gmail.com.pub` and `sitaramc@desktop.pub`. We -do that by requiring that the multi-key suffix you use (like "desktop" and -"laptop") should not have a `"."` in it. If it does, it looks like an email -address. The following table lists sample pubkey filenames and the -corresponding derived usernames (which is what goes into the -`conf/gitolite.conf` file): - - * old style multikeys; not mistaken for emails because there is no "." in - hostname part - - sitaramc.pub sitaramc - sitaramc@laptop.pub sitaramc - sitaramc@desktop.pub sitaramc - - * new style, email keys; there is a "." in hostname part; so it's an email - address - - sitaramc@gmail.com.pub sitaramc@gmail.com - - * multikeys *with* email address - - sitaramc@gmail.com@laptop.pub sitaramc@gmail.com - sitaramc@gmail.com@desktop.pub sitaramc@gmail.com - -#### support for git installed outside default PATH - -The normal solution is to add to the system default PATH somehow, either by -munging `/etc/profile` or by enabling `PermitUserEnvironment` in -`/etc/ssh/sshd_config` and then setting the PATH in `~/.ssh/.environment`. -All these are security risks because they allow a lot more than just you and -your git install :-) - -And if you don't have root, you can't do this anyway. - -The only solution till now has been to ask every client to set the config -parameters `remote..receivepack` and `remote..uploadpack`. But -telling *every* client to do so is a pain... - -Gitolite lets you specify the directory in which git binaries are to be found, -via a new variable (`$GIT_PATH`) in the "rc" file. If this variable is -non-empty, it will be appended to the PATH environment variable before -attempting to run git stuff. - -Very easy, very simple, and completely transparent to the users :-) - - - -#### what repos do I have access to? - -Sometimes there are too many repos, maybe even named similarly, or with the -potential for typos, confusion about hyphens/underscores or upper/lower case, -etc. You'd just like a simple way to know what repos you have access to. - -Easy! Just use ssh and try to log in as if you were attempting to get a -shell: - - $ ssh gitolite info - PTY allocation request failed on channel 0 - hello sitaram, the gitolite version here is v0.6-17-g94ed189 - you have the following permissions: - R W Anu-WSD - R ROtest - R W SecureBrowse - R W entrans - R W git-notes - R W gitolite - R W gitolite-admin - R W indic_web_input - R W proxy - @ @ testing - R W vkc - -Note that until this version, we used to put out an ugly `need -SSH_ORIGINAL_COMMAND` error, just like gitosis used to. All we did is put -that code path to better use :-) - -#### "exclude" (or "deny") rules - -Here is an illustrative explanation of "deny" rules. However, please be sure -to read the "DENY/EXCLUDE RULES" section in `conf/example.conf` for important -notes/caveats before using "deny" rules. - -Take a look at the following snippet, which *seems* to say that "bruce" can -write versioned tags (anything containing `refs/tags/v[0-9]`), but the other -staffers can't: - - @staff = bruce whitfield martin - [... and later ...] - RW refs/tags/v[0-9] = bruce - RW refs/tags = @staff - -But that's not how the matching works. As long as any refex matches the -refname being updated, it's a "yes". Since the second refex (which says -"anything containing `refs/tags`") is a superset of the first one, it lets -anyone on `@staff` create versioned tags, not just Bruce. - -One way to fix this is to allow "excludes" -- some changes in syntax, combined -with a rigorous, ordered, interpretation would do it. - -Let's recap the **existing semantics**: - -> the first matching refex that has the permission you're looking for (`W` -> or `+`), results in success. A fallthrough results in failure - -Here are the **new semantics**, with changes from the "main" one in bold: - -> the first matching refex that has the permission you're looking for (`W` -> or `+`) **or a minus (`-`)**, results in success **or failure, -> respectively**. A fallthrough **also** results in failure - -So the example we started with becomes, if you use "deny" rules: - - RW refs/tags/v[0-9] = bruce - - refs/tags/v[0-9] = @staff - RW refs/tags = @staff - -And here's how it works: - - * for non-version tags, only the 3rd rule matches, so anyone on staff can - push them - * for version tags by bruce, the first rule matches so he can push them - * for version tags by staffers *other than bruce*, the second rule matches - before the third one, and it has a `-` as the permission, so the push - fails - -#### "personal" branches - -"personal" branches are great for corporate environments, where -unauthenticated pull/clone is a no-no. Since a dev workstation cannot do -authentication, even work shared just between 2 devs has to go *via* the -server. This causes the same branch name clutter as in a centralised VCS, -plus setting up permissions for this becomes a chore for the admin. - -gitolite lets you define a "personal" or "scratch" namespace prefix for -each developer (e.g., `refs/personal//*`), with full -permissions for that dev and read-only for everyone else. And you get -this without adding a single line to the access config file -- pretty -much fire and forget as far as the admin is concerned, even if there is -constant churn in the project teams. - -Not bad for something that took just *one* line of code to implement. -And that's one clean, readable, line, by the way ;-) - -The admin would set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate -this to all users. It could be something like `refs/heads/personal`, which -means all such branches will show up in `git branch` lookups and `git clone` -will fetch them. Or he could use, say, `refs/personal`, which means it won't -show up in any normal "branch-y" commands and stuff, and generally be much -less noisy. - -**Note that a user who has NO write access cannot have personal branches**; if -you read the section (above) on "two levels of access rights checking" you'll -understand why. - -For instance, in the following example, `user3` cannot push to any -`refs/heads/personal/user3/*` branches because the first level check stops him -cold: - - # assume $PERSONAL = 'refs/heads/personal' in ~/.gitolite.rc - repo myrepo - RW+ master = sitaram - RW+ release = qa_guy - RW = user1 user2 - R = user3 - -If we relax that check, *any* access becomes *write* access. Yes it will be -caught later, by the hook, but it's good practice to catch things in multiple -places. - -If you want `user3` to have his own personal branch, but without write access -to any of the "real" branches (like "master", "release", etc.), just use a -dummy branch. Choose a name that will never exist in practice, or even if -someone creates it, we don't care. For example, this will get him past the -first check: - - RW dummy = user3 - -Just don't *show* the user this config file; it might sound insulting :-) - -#### custom hooks and custom git config - -You can specify hooks that you want to propagate to all repos, as well as -per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and -`conf/example.conf` for details. +### advanced features #### repos named with wildcards @@ -642,9 +661,9 @@ who has shell access anyway). Please see the config files (both of them) for examples and usage. -### design choices +## design choices -#### keeping the parser and the access control separate +### keeping the parser and the access control separate There are two programs concerned with access control: From 65b8c0c48a9c954533ee3ececb1401749b323023 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 9 Feb 2010 17:00:01 +0530 Subject: [PATCH 251/850] make $bindir absolute --- src/gl-auth-command | 1 + src/gl-compile-conf | 1 + 2 files changed, 2 insertions(+) diff --git a/src/gl-auth-command b/src/gl-auth-command index f7f415a..d1a7a84 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -32,6 +32,7 @@ our %repos; # the common setup module is in the same directory as this running program is my $bindir = $0; $bindir =~ s/\/[^\/]+$//; +$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//; require "$bindir/gitolite.pm"; # ask where the rc file is, get it, and "do" it diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 6a02107..42c0143 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -68,6 +68,7 @@ our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTION # the common setup module is in the same directory as this running program is my $bindir = $0; $bindir =~ s/\/[^\/]+$//; +$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//; require "$bindir/gitolite.pm"; # ask where the rc file is, get it, and "do" it From 74d70e3b9f36455dc14331003e55a977551b6933 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 9 Feb 2010 19:37:37 +0530 Subject: [PATCH 252/850] move hooks out of src src/hooks is now hooks/common src/ga... is now hooks/gitolite-admin/post-update --- {src/hooks => hooks/common}/update | 0 .../gitolite-admin/post-update | 0 src/gl-auth-command | 2 +- src/gl-compile-conf | 2 +- src/gl-easy-install | 6 +++--- src/gl-install | 10 +++++----- 6 files changed, 10 insertions(+), 10 deletions(-) rename {src/hooks => hooks/common}/update (100%) rename src/ga-post-update-hook => hooks/gitolite-admin/post-update (100%) diff --git a/src/hooks/update b/hooks/common/update similarity index 100% rename from src/hooks/update rename to hooks/common/update diff --git a/src/ga-post-update-hook b/hooks/gitolite-admin/post-update similarity index 100% rename from src/ga-post-update-hook rename to hooks/gitolite-admin/post-update diff --git a/src/gl-auth-command b/src/gl-auth-command index d1a7a84..c18d880 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -176,7 +176,7 @@ if ( -d "$repo_base_abs/$repo.git" ) { # auto-vivify new repo if you have C access (and wildrepos is on) if ( $GL_WILDREPOS and $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'} ) { wrap_chdir("$repo_base_abs"); - new_repo($repo, "$GL_ADMINDIR/src/hooks", $user); + new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); wrap_chdir($ENV{HOME}); } } diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 42c0143..e7fcb9c 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -396,7 +396,7 @@ for my $repo (sort keys %repos) { next if $repo =~ m(^EXTCMD/); # these are not real repos unless (-d "$repo.git") { print STDERR "creating $repo...\n"; - new_repo($repo, "$GL_ADMINDIR/src/hooks"); + new_repo($repo, "$GL_ADMINDIR/hooks/common"); # new_repo would have chdir'd us away; come back wrap_chdir("$repo_base_abs"); } diff --git a/src/gl-easy-install b/src/gl-easy-install index 35c31e2..a8dc013 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -169,12 +169,12 @@ basic_sanity() { # MANUAL: make sure you're in the gitolite directory, at the top level. # The following files should all be visible: - ls src/ga-post-update-hook \ + ls hooks/gitolite-admin/post-update \ + hooks/common/update \ src/gitolite.pm \ src/gl-install \ src/gl-auth-command \ src/gl-compile-conf \ - src/hooks/update \ conf/example.conf \ conf/example.gitolite.rc >/dev/null || die "cant find at least some files in gitolite sources/config; aborting" @@ -282,7 +282,7 @@ copy_gl() { # have to create the directory first. ssh -p $port $user@$host mkdir -p gitolite-install - scp $quiet -P $port -r src conf doc $user@$host:gitolite-install/ + scp $quiet -P $port -r src conf doc hooks $user@$host:gitolite-install/ rm -f src/VERSION # MANUAL: now log on to the server (ssh git@server) and get a command diff --git a/src/gl-install b/src/gl-install index 8a59569..dbebe22 100755 --- a/src/gl-install +++ b/src/gl-install @@ -48,12 +48,12 @@ my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" wrap_mkdir($repo_base_abs); wrap_mkdir($GL_ADMINDIR); # mkdir $GL_ADMINDIR's subdirs -for my $dir qw(conf doc keydir logs src) { +for my $dir qw(conf doc keydir logs src hooks hooks/common hooks/gitolite-admin) { wrap_mkdir("$GL_ADMINDIR/$dir"); } # "src" and "doc" will be overwritten on each install, but not conf -system("cp -R src doc $GL_ADMINDIR"); +system("cp -R src doc hooks $GL_ADMINDIR"); unless (-f $GL_CONF) { print < Date: Wed, 10 Feb 2010 09:24:27 +0530 Subject: [PATCH 253/850] install: initial create of glrc should not assume PWD is project root make it work regardless of how it is invoked, though we *do* assume ../conf/example.gitolite.rc exists --- src/gl-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-install b/src/gl-install index dbebe22..01a2317 100755 --- a/src/gl-install +++ b/src/gl-install @@ -31,7 +31,7 @@ require "$bindir/gitolite.pm"; unless ($ENV{GL_RC}) { # doesn't exist. Copy it across, tell user to edit it and come back my $glrc = $ENV{HOME} . "/.gitolite.rc"; - system("cp conf/example.gitolite.rc $glrc"); + system("cp $bindir/../conf/example.gitolite.rc $glrc"); print "created $glrc\n"; print "please edit it, change the paths if you wish to, and RERUN THIS SCRIPT\n"; exit; From 927b6bb1aaf6d1010d2d17261d37af6247309900 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 10 Feb 2010 10:29:49 +0530 Subject: [PATCH 254/850] dps: make install aware of distro-based setup gl-install copies - the initial rc file to ~/.gitolite.rc if it doesn't exist - src and hooks to GL_ADMINDIR Make it aware of a package-based setup sequence, where the above two change somewhat; see code diff. This should be the last bit of change needed to prepare gitolite setup so that a distro package maintainer does not have to fiddle too much with code inside. (What remains is docs, and a setup script for server-side use, to replace the latter part of easy install) --- src/gl-install | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/gl-install b/src/gl-install index 01a2317..13798ae 100755 --- a/src/gl-install +++ b/src/gl-install @@ -3,7 +3,7 @@ use strict; use warnings; -our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH); +our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS); # setup quiet mode if asked; please do not use this when running manually open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); @@ -31,7 +31,11 @@ require "$bindir/gitolite.pm"; unless ($ENV{GL_RC}) { # doesn't exist. Copy it across, tell user to edit it and come back my $glrc = $ENV{HOME} . "/.gitolite.rc"; - system("cp $bindir/../conf/example.gitolite.rc $glrc"); + if ($GL_PACKAGE_CONF) { + system("cp $GL_PACKAGE_CONF/example.gitolite.rc $glrc"); + } else { + system("cp $bindir/../conf/example.gitolite.rc $glrc"); + } print "created $glrc\n"; print "please edit it, change the paths if you wish to, and RERUN THIS SCRIPT\n"; exit; @@ -49,13 +53,18 @@ wrap_mkdir($repo_base_abs); wrap_mkdir($GL_ADMINDIR); # mkdir $GL_ADMINDIR's subdirs for my $dir qw(conf doc keydir logs src hooks hooks/common hooks/gitolite-admin) { + # some of them will stay empty; too lazy to fix right now ;-) wrap_mkdir("$GL_ADMINDIR/$dir"); } # "src" and "doc" will be overwritten on each install, but not conf -system("cp -R src doc hooks $GL_ADMINDIR"); +if ($GL_PACKAGE_HOOKS) { + system("cp -R $GL_PACKAGE_HOOKS $GL_ADMINDIR"); +} else { + system("cp -R $bindir/../src $bindir/../doc $bindir/../hooks $GL_ADMINDIR"); +} -unless (-f $GL_CONF) { +unless (-f $GL_CONF or $GL_PACKAGE_CONF) { print < Date: Mon, 8 Feb 2010 20:01:14 +0530 Subject: [PATCH 255/850] added server-side setup script --- src/gl-setup | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100755 src/gl-setup diff --git a/src/gl-setup b/src/gl-setup new file mode 100755 index 0000000..b48975c --- /dev/null +++ b/src/gl-setup @@ -0,0 +1,84 @@ +#!/bin/sh + +GL_PACKAGE_CONF=/tmp/share/gitolite/conf +# must be the same as the value for the same variable in +# $GL_PACKAGE_CONF/example.gitolite.rc. Sorry about the catch-22 :) + +# TODO need to fix for portability to ksh and so on +# TODO need to get the version in there somehow + +# This program is meant to be completely non-interactive, suitable for running +# server-side from a "post RPM/DEB install" script, or manually by users. +# Please see the doc/0-user-setup.mkd for details. + +# usage: +# $0 [foo.pub] + +# The pubkey filename must end with ".pub" and is mandatory when you first run +# this command. Otherwise it is optional, and can be used to override a +# pubkey file if you happen to have lost all gitolite-access to the repos (but +# do have shell access via some other means) + +die() { echo "$@"; echo death at line number ${BASH_LINENO[0]}; exit 1; } + +pubkey_file=$1 +admin_name= +if [[ -n $pubkey_file ]] +then + [[ $pubkey_file =~ .pub$ ]] || die "$pubkey_file must end in .pub" + [[ -f $pubkey_file ]] || die "cant find $pubkey_file" + admin_name=$(basename $pubkey_file .pub) +fi + +if [[ -f ~/.gitolite.rc ]] +then + perl -ne 's/^\s+//; s/[\s=].*//; print if /^\$/;' < $GL_PACKAGE_CONF/example.gitolite.rc | sort > .newvars + perl -ne 's/^\s+//; s/[\s=].*//; print if /^\$/;' < ~/.gitolite.rc | sort > .oldvars + comm -23 .newvars .oldvars > .diffvars + if [[ -s .diffvars ]] + then + cp $GL_PACKAGE_CONF/example.gitolite.rc ~/.gitolite.rc.new + echo new version of the rc file saved in ~/.gitolite.rc.new + echo + echo please update ~/.gitolite.rc manually if you need features + echo controlled by any of the following variables: + echo ---- + sed -e 's/^/ /' < .diffvars + echo ---- + fi + rm -f .newvars .oldvars .diffvars +else + [[ -n $pubkey_file ]] || die "looks like first run -- I need a pubkey file" + cp $GL_PACKAGE_CONF/example.gitolite.rc ~/.gitolite.rc +fi + +gl-install -q + +GL_ADMINDIR=$(cd;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR') +REPO_BASE=$( cd;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ) + +[[ -f $GL_ADMINDIR/conf/gitolite.conf ]] || { + echo DEBUG NO DEFAULT CONF FOUND .. CREATING .. >&2 + cat < $GL_ADMINDIR/conf/gitolite.conf + repo gitolite-admin + RW+ = $admin_name + + repo testing + RW+ = @all +EOF +} +cp $pubkey_file $GL_ADMINDIR/keydir + +touch $HOME/.ssh/authorized_keys +gl-compile-conf -q + +# setup push-to-admin +od=$PWD +cd; cd $REPO_BASE/gitolite-admin.git +GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir +GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start +cd $od + +# now that the admin repo is created, you have to set the hooks properly; best +# do it by running install again +gl-install -q From 06d8ab4c18d158577cb1fe561a062fc3543c4f63 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 10 Feb 2010 12:35:01 +0530 Subject: [PATCH 256/850] make VERSION work in both types of setups The old install method will now use conf/VERSION instead of src/VERSION everywhere. The new one, if you use the builtin make file to "make branch.tar" will also create just such a file --- Makefile | 8 ++++---- src/gitolite.pm | 4 ++-- src/gl-easy-install | 7 +++---- src/gl-install | 3 ++- src/gl-setup | 3 +-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 730fa72..7780900 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,10 @@ # Note: I'm not sure if that "-r" is a GNU tar extension... .GITOLITE-VERSION: - @touch .GITOLITE-VERSION + @touch conf/VERSION %.tar: .GITOLITE-VERSION - git describe --all --long $* > .GITOLITE-VERSION + git describe --tags --long $* > conf/VERSION git archive $* > $@ - tar -r -f $@ .GITOLITE-VERSION - rm .GITOLITE-VERSION + tar -r -f $@ conf/VERSION + rm conf/VERSION diff --git a/src/gitolite.pm b/src/gitolite.pm index 5638d05..15728cc 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -34,7 +34,7 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple patter our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file -our ($REPO_UMASK, $GL_WILDREPOS); +our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF); our %repos; # ---------------------------------------------------------------------------- @@ -282,7 +282,7 @@ sub report_basic # send back some useful info if no command was given print "hello $user, the gitolite version here is "; - system("cat", "$GL_ADMINDIR/src/VERSION"); + system("cat", ($GL_PACKAGE_CONF || "$GL_ADMINDIR/conf") . "/VERSION"); print "\ryou have the following permissions:\n\r"; for my $r (sort keys %repos) { my $perm .= ( $repos{$r}{C}{'@all'} ? ' @' : ( $repos{$r}{C}{$user} ? ' C' : ' ' ) ); diff --git a/src/gl-easy-install b/src/gl-easy-install index a8dc013..989f3a6 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -196,12 +196,12 @@ version_info() { # MANUAL: if needed, make a note of the version you are upgrading from, and to # record which version is being sent across; we assume it's HEAD - git describe --tags --long HEAD 2>/dev/null > src/VERSION || echo '(unknown)' > src/VERSION + git describe --tags --long HEAD 2>/dev/null > conf/VERSION || echo '(unknown)' > conf/VERSION # what was the old version there? export upgrade_details="you are upgrading \ - $(ssh -p $port $user@$host cat gitolite-install/src/VERSION 2>/dev/null || echo '(or installing first-time)' ) \ - to $(cat src/VERSION)" + $(ssh -p $port $user@$host cat gitolite-install/conf/VERSION 2>/dev/null || echo '(or installing first-time)' ) \ + to $(cat conf/VERSION)" prompt "$upgrade_details" "$v_upgrade_details" } @@ -283,7 +283,6 @@ copy_gl() { ssh -p $port $user@$host mkdir -p gitolite-install scp $quiet -P $port -r src conf doc hooks $user@$host:gitolite-install/ - rm -f src/VERSION # MANUAL: now log on to the server (ssh git@server) and get a command # line. This step is for your convenience; the script does it all from diff --git a/src/gl-install b/src/gl-install index 13798ae..485683a 100755 --- a/src/gl-install +++ b/src/gl-install @@ -62,6 +62,7 @@ if ($GL_PACKAGE_HOOKS) { system("cp -R $GL_PACKAGE_HOOKS $GL_ADMINDIR"); } else { system("cp -R $bindir/../src $bindir/../doc $bindir/../hooks $GL_ADMINDIR"); + system("cp $bindir/../conf/VERSION $GL_ADMINDIR/conf"); } unless (-f $GL_CONF or $GL_PACKAGE_CONF) { @@ -95,7 +96,7 @@ if ( -d "gitolite-admin.git/hooks" ) { } # fixup program renames -for my $oldname qw(pta-hook.sh conf-convert.pl 00-easy-install.sh 99-emergency-addkey.sh install.pl update-hook.pl hooks/update ga-post-update-hook) { +for my $oldname qw(pta-hook.sh conf-convert.pl 00-easy-install.sh 99-emergency-addkey.sh install.pl update-hook.pl hooks/update ga-post-update-hook VERSION) { unlink "$GL_ADMINDIR/src/$oldname"; unlink "$ENV{HOME}/gitolite-install/src/$oldname"; } diff --git a/src/gl-setup b/src/gl-setup index b48975c..25e4e0f 100755 --- a/src/gl-setup +++ b/src/gl-setup @@ -58,7 +58,6 @@ GL_ADMINDIR=$(cd;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR') REPO_BASE=$( cd;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ) [[ -f $GL_ADMINDIR/conf/gitolite.conf ]] || { - echo DEBUG NO DEFAULT CONF FOUND .. CREATING .. >&2 cat < $GL_ADMINDIR/conf/gitolite.conf repo gitolite-admin RW+ = $admin_name @@ -67,7 +66,7 @@ REPO_BASE=$( cd;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ) RW+ = @all EOF } -cp $pubkey_file $GL_ADMINDIR/keydir +[[ -n $pubkey_file ]] && cp $pubkey_file $GL_ADMINDIR/keydir touch $HOME/.ssh/authorized_keys gl-compile-conf -q From e674a7c64a04812a55329f66b19be4329e27d759 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 10 Feb 2010 16:19:20 +0530 Subject: [PATCH 257/850] (package maintainers read this) install doc updated (about this commit) The install doc now describes both the ways of installing gitolite. It also has a handy appendix for package maintainers describing what they need to do. (about the "dps" -- distro packaging support -- commit series) This commit is the last in the chain meant to make gitolite more friendly for package maintainers. Frankly, I never really thought gitolite would get big enough or important enough for someone to package it, and I always did just the bare minimum I needed to get it working, first for myself, then anyone who hopped onto #git and asked. As a result, it had some quirks in terms of what is expected where and so on... Luckily, it didn't take a lot of changes to fix it, and this series of commits should help make it very easy to package gitolite for system-wide use. --- doc/0-INSTALL.mkd | 130 +++++++++++++++++++++++++++++++++++++++------- src/gitolite.pm | 5 +- src/gl-install | 5 ++ 3 files changed, 121 insertions(+), 19 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index af4022f..c0929ac 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -2,30 +2,59 @@ [Update 2009-11-18: easy install now works from msysgit also!] +Gitolite is somewhat unusual as far as "server" software goes -- every userid +on the system is a potential "gitolite host" and can install his own version +if he chooses to. + This document tells you how to install gitolite. After the install is done, you may want to see the [admin document][admin] for adding users, repos, etc. [admin]: http://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd -There's an easy install script that requires bash (**strongly** recommended), -but if you have no bash or you're on one of the legacy Unixes there's a -slightly more manual process. Both are explained here. +[[TOC]] In this document: - * easy install + * install methods + * user install * typical example run * advantages over the older install methods * disadvantages - * manual install - * upgrades - * other notes + * upgrades + * other notes + * system install / user setup * next steps - * appendix: server and client requirements + * appendix A: server and client requirements for user install + * server + * install workstation + * admin workstation(s) + * appendix B: NOTE TO PACKAGE MAINTAINERS ---- -### easy install +### install methods + +There are 2 ways to install gitolite: The **user-install** mode was the +traditional way, and is used when *any* of the following is true: + + * you don't have root on your "server" (some types of hosting setups, many + corporate paranoia setups ;-) + * your server distro does not have gitolite in its package repositories + * your server distro's package repositories have an old version of gitolite + * you want to stay current with the latest gitolite versions + * your server is not Linux (maybe AIX, or Solaris, etc.) + +The "user install" section describes this method. + +The **system-install followed by user-setup** mode is used when you (or +someone who has root) has installed an RPM or DEB of gitolite and you intend +to use that version. + +The "system install / user setup" section describes this method. + +---- + +### user install There is an easy install script that makes installing very easy for the common case. **This script will setup everything on the server, but you have to run @@ -43,6 +72,7 @@ Assumptions/pre-requisites: `ssh-copy-id` in that file for instructions * you have a clone or an archive of gitolite somewhere on your workstation * if you don't have one, just run `git clone git://github.com/sitaramc/gitolite` + * your workstation has bash (even msysgit bash will do) Once you have all this, just `cd` to that clone and run `src/gl-easy-install` and follow the prompts! (Running it without any arguments shows you usage @@ -75,13 +105,7 @@ actually doing, I suggest you skip the `-q`. * need a recent bash -### manual install - -If you don't have bash, it's not very complicated to do it manually. Just -open the file `src/gl-easy-install` in a nice, syntax coloring, text -editor, and follow the instructions marked "MANUAL" :-) - -### upgrades +#### upgrades Upgrading gitolite is easy. @@ -97,7 +121,7 @@ way. I decided that it is not possible to **safely** let an upgrade do something meaningful with them -- fiddling with existing config files (as opposed to merely creating one which did not exist) is best left to a human. -### other notes +#### other notes * if you run `src/gl-easy-install` without the `-q` option, you will be given a chance to edit `~/.gitolite.rc`. You can change any options (such @@ -105,6 +129,36 @@ opposed to merely creating one which did not exist) is best left to a human. *don't* have to know perl to do so, it's fairly easy to guess in this limited case. +### system install / user setup + +In this mode a system administrator installs gitolite using the server's +distro package mechanism (yum install, apt-get install, etc). + +Once this is done, you as a user must run a command like this (unlike in the +"user install" mode, this is done directly on the server): + + gl-setup yourname.pub + +where yourname.pub is a copy of a public key from your workstation. The first +time you run this, it will create a "gitolite-admin" repo and populate it with +the right configuration for whoever has the corresponding private key to +clone and push it. In other words, that person is the administrator for this +particular gitolite instance. + +If your system administrator upgrades gitolite itself, things will usually +just work without any change; you should not have to do anything special. +However, some new features may require additional settings in your +`~/.gitolite.rc` file. + +Finally, in the rare case that you managed to lose your keys to the admin repo +and want to supply a new pubkey, you can use this command to replace any such +key. Could be useful in an emergency -- just get your new "yourname.pub" to +the server and run the same command as above. + +**IMPORTANT**: there are two variables in the `~/.gitolite.rc` file: +`$GL_PACKAGE_CONF` and `$GL_PACKAGE_HOOKS`. If you remove or change either of +them, expect trouble :-) + ### next steps The last message produced by the easy install script should tell you how to @@ -113,7 +167,7 @@ document. -### appendix: server and client requirements +### appendix A: server and client requirements for user install There are 3 machines *potentially* involved in installing and administering gitolite. @@ -167,3 +221,43 @@ Which means all this can be done from *any* machine. You'll normally do it from the same machine you used to install gitolite, but it doesn't have to be the same one, as long as your pubkey has been added and permissions given to allow you to push to the gitolite-admin repo. + +### appendix B: NOTE TO PACKAGE MAINTAINERS + +Here's how you'd package gitolite. In the following description, location "X" +can be, say, `/usr/share/gitolite/conf` or some such, and similarly location +"Y" can be perhaps `/usr/share/gitolite/hooks`. It's upto your distro +policies where they are. + +These are the content changes needed (no trailing slashes in the location +values please): + + * `gl-setup` should have the following line: + + GL_PACKAGE_CONF="X" + + * `example.gitolite.rc` should have the following lines: + + $GL_PACKAGE_CONF="X"; + $GL_PACKAGE_HOOKS="Y"; + +This is where the files should be installed: + + * everything in "src" goes somewhere on the PATH + * everything in "conf" goes to location "X" + * everything in "hooks" goes to location "Y" + +You might also want to delete the `gl-easy-install` script, since that is +meant for a totally different mode of installation and probably would *not* +work if a user tried to run it :-) + +On the initial install, you could also choose to setup a userid called +"gitolite", and run "gl-setup" as that user; however I do not know how you +would come up with the initial pubkey that is needed. Anyway, the point is +that the "gitolite" user is no more special than any other in terms of hosting +gitolite. Any user can host it by just running "gl-setup". + +When you upgrade, just overwrite all the files; it'll all just work. In fact, +other than the initial "gl-setup" run, the only time a gitolite hosting user +has to actually do anything is to edit their own `~/.gitolite.rc` file if they +want to enable or disable specific features. diff --git a/src/gitolite.pm b/src/gitolite.pm index 15728cc..0bbd604 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -34,7 +34,7 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple patter our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file -our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF); +our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS); our %repos; # ---------------------------------------------------------------------------- @@ -148,6 +148,9 @@ sub new_repo } # propagate our own, plus any local admin-defined, hooks ln_sf($hooks_dir, "*", "hooks"); + # in case of package install, GL_ADMINDIR is no longer the top cop; + # override with the package hooks + ln_sf("$GL_PACKAGE_HOOKS/common", "*", "hooks") if $GL_PACKAGE_HOOKS; chmod 0755, "hooks/update"; } diff --git a/src/gl-install b/src/gl-install index 485683a..b163439 100755 --- a/src/gl-install +++ b/src/gl-install @@ -83,6 +83,9 @@ for my $repo (`find . -type d -name "*.git"`) { chomp ($repo); # propagate our own, plus any local admin-defined, hooks ln_sf("$GL_ADMINDIR/hooks/common", "*", "$repo/hooks"); + # in case of package install, GL_ADMINDIR is no longer the top cop; + # override with the package hooks + ln_sf("$GL_PACKAGE_HOOKS/common", "*", "$repo/hooks") if $GL_PACKAGE_HOOKS; chmod 0755, "$repo/hooks/update"; } @@ -92,6 +95,8 @@ if ( -d "gitolite-admin.git/hooks" ) { unlink "gitolite-admin.git/hooks/post-update"; symlink "$GL_ADMINDIR/hooks/gitolite-admin/post-update", "gitolite-admin.git/hooks/post-update" or die "could not symlink post-update hook\n"; + # ditto... (see previous block) + ln_sf("$GL_PACKAGE_HOOKS/gitolite-admin", "post-update", "gitolite-admin.git/hooks") if $GL_PACKAGE_HOOKS; chmod 0755, "gitolite-admin.git/hooks/post-update"; } From 998ff2d13b9aa913be32c26460d7c54391f6179a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 13 Feb 2010 19:18:28 +0530 Subject: [PATCH 258/850] doc/1 minor fix thanks to bremner for catching this... --- doc/1-migrate.mkd | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/1-migrate.mkd b/doc/1-migrate.mkd index c09fe13..641f9c5 100644 --- a/doc/1-migrate.mkd +++ b/doc/1-migrate.mkd @@ -42,11 +42,14 @@ Now, log off the server and get back to the client: Make sure that you **don't** change the default path for `$REPO_BASE` if you edit the config file! -2. **convert** your gitosis config file. Substitute the path for your - gitosis-admin clone in `$GSAC` below, and similarly the path for your - gito**lite**-admin clone in `$GLAC` + This will give you a gitolite config that has the required entries for the + "gitolite-admin" repo. - src/gl-conf-convert < $GSAC/gitosis.conf > $GLAC/gitolite.conf +2. **convert** your gitosis config file and append it to your gitolite config + file. Substitute the path for your gitosis-admin clone in `$GSAC` below, + and similarly the path for your gito**lite**-admin clone in `$GLAC` + + src/gl-conf-convert < $GSAC/gitosis.conf >> $GLAC/gitolite.conf Be sure to check the file to make sure it converted correctly From 690604d79a5e8f35f6e39c0154cb6fdd3f211adf Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 13 Feb 2010 20:00:45 +0530 Subject: [PATCH 259/850] compile: users and repos have groups... why not refs? this came up in some other discussion with bremner. As usual I said no I won't do it because I don't see any real need. ...then I realised it's just one line :) --- src/gl-compile-conf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 6a02107..371e912 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -206,6 +206,7 @@ sub parse_conf_file { my $perms = $1; my @refs; @refs = split(' ', $2) if $2; + @refs = expand_list ( @refs ); my @users = split ' ', $3; die "wildrepos disabled, cant use 'C' in config\n" if $perms eq 'C' and not $GL_WILDREPOS; From 83a017f8845abcc88b98eed27e4532b819f77a21 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 14 Feb 2010 09:51:51 +0530 Subject: [PATCH 260/850] htpassword: disallow empty passwords [TODO: allow a callback for a password checking function, such as "passwd_policy_check". Question is where the function would go. ~/.gitolite.rc is the only possible place among the current set of files but I'd rather leave that as a list of simple name=value lines for all sorts of reasons. So maybe something like ~/.gitolite.pm (analogous to the "gitolite.pm" in the sources I supply), which would get "require'd" if found, and would contain all user-defined functions like this one... needs some thinking about] --- src/gitolite.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gitolite.pm b/src/gitolite.pm index 5638d05..6f8025c 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -457,6 +457,7 @@ EOFhtp my $password = <>; $password =~ s/[\n\r]*$//; + die "empty passwords are not allowed\n" unless $password; my $rc = system("htpasswd", "-b", $HTPASSWD_FILE, $ENV{GL_USER}, $password); die "htpasswd command seems to have failed with $rc return code...\n" if $rc; } From 6f740339e4902f3803ca1e4c2cf61728eb30dfa5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 16 Feb 2010 04:57:14 +0530 Subject: [PATCH 261/850] doc/3 last reorg missed moving some anchors and preamble text --- doc/3-faq-tips-etc.mkd | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 784f58b..684086a 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -156,12 +156,18 @@ plain "git archive", because the Makefile adds a file called make master.tar # or maybe "make pu.tar" - + ## features +Apart from the big ones listed in the top level README, and subjective ones +like "better config file format", gitolite has evolved to have many useful +fearures than the original goal of "gitosis + branch-level access control". + ### syntax and normal usage + + #### simpler syntax The basic syntax is simpler and cleaner but it goes beyond that: **you can @@ -205,6 +211,8 @@ do not worry that this causes some duplication or inefficiency. It doesn't See the "specify gitweb/daemon access" section below for one more example. + + #### one user, many keys I have a laptop and a desktop I need to access the server from. I have @@ -250,6 +258,8 @@ corresponding derived usernames (which is what goes into the ### security, access control, and auditing + + #### two levels of access rights checking Gitolite has two levels of access checks. The **first check** is what I will @@ -285,12 +295,6 @@ any of the refexes match, the push succeeds. If none of them match, it fails. Gitolite also allows "exclude" or "deny" rules. See later in this document for details. -Apart from the big ones listed in the top level README, and subjective ones -like "better config file format", gitolite has evolved to have many useful -fearures than the original goal of "gitosis + branch-level access control". - - - #### better logging If you have been too liberal with the permission to rewind, it has built-in @@ -317,8 +321,6 @@ The other parts of the log line are the name of the repo, the refname being updated, the user updating it, and the refex pattern (from the config file) that matched, in case you need to debug the config file itself. - - #### "exclude" (or "deny") rules Here is an illustrative explanation of "deny" rules. However, please be sure @@ -384,10 +386,10 @@ access control for their own pieces. See [doc/5-delegation.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/5-delegation.mkd) for details. - - ### convenience features + + #### what repos do I have access to? Sometimes there are too many repos, maybe even named similarly, or with the @@ -451,8 +453,6 @@ attempting to run git stuff. Very easy, very simple, and completely transparent to the users :-) - - #### "personal" branches "personal" branches are great for corporate environments, where @@ -513,6 +513,8 @@ You can specify hooks that you want to propagate to all repos, as well as per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and `conf/example.conf` for details. + + ### helping with gitweb Although gitweb is a completely separate program, gitolite can do quite a From 8d382a6d25d5531e26e372d767964d982aef4058 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 16 Feb 2010 05:02:14 +0530 Subject: [PATCH 262/850] doc/6 now has anchors --- doc/6-ssh-troubleshooting.mkd | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 8189af8..83d908b 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -43,6 +43,8 @@ are using your shell access -- instead of running via `/some/path/gl-auth-command ` it is just going to bash and working from there!] + + ### basic ssh troubleshooting [glb]: http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh @@ -182,6 +184,8 @@ you had command line access to the server *before* you were added as a gitolite user. If you send that same key to your gitolite admin to include in the admin repo, it won't work. For reasons why, see below. + + ### details Here's how it all hangs together. @@ -348,6 +352,8 @@ that should have enough info to get you going (but it helps to know ssh well): That should do it. + + ### more complex ssh setups What do you need to know in order to create more complex ssh setups (for @@ -373,6 +379,8 @@ instance if you have *two* gitolite servers you are administering)? * now access one server's repos as `gitolite:reponame.git` and the other server's repos as `gitolite2:reponame.git`. + + ### giving shell access to gitolite users We've managed (thanks to an idea from Jesse Keating) to make it possible for a From 087aa274c6ef602e99e24acb6a9a27036ad11cf9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 16 Feb 2010 06:39:27 +0530 Subject: [PATCH 263/850] doc/0: added uninstall instructions --- doc/0-INSTALL.mkd | 54 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index af4022f..88ba053 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -21,7 +21,8 @@ In this document: * upgrades * other notes * next steps - * appendix: server and client requirements + * appendix A: server and client requirements + * appendix B: uninstalling gitolite ---- @@ -113,7 +114,7 @@ document. -### appendix: server and client requirements +### appendix A: server and client requirements There are 3 machines *potentially* involved in installing and administering gitolite. @@ -167,3 +168,52 @@ Which means all this can be done from *any* machine. You'll normally do it from the same machine you used to install gitolite, but it doesn't have to be the same one, as long as your pubkey has been added and permissions given to allow you to push to the gitolite-admin repo. + + + +### uninstalling gitolite + +Sometimes you might find gitolite is overkill -- you have only one user +(yourself) pushing maybe. Or maybe gitolite is just not enough -- you want a +web-based front end that users can use to manage their keys themselves, etc., +in which case you'd probably switch to [github][g1], [girocco][g2], +[indefero][g3] or [gitorious][g4]. Either way, you'd like to uninstall +gitolite. + +[g1]: http://github.com +[g2]: http://repo.or.cz/w/girocco.git +[g3]: http://www.indefero.net/ +[g4]: http://gitorious.com/ + +Uninstalling gitolite is fairly easy. Just log on to the server and do the +following (assuming `$REPO_BASE` in the rc file was left at its default of +`~/repositories`; if not, adjust accordingly): + + * edit `~/.ssh/authorized_keys` and delete the `# gitolite start` and `# + gitolite end` markers and all the lines between them. This will prevent + any of your users from attempting a push while you are doing this. + + If you are the only user, and/or *need* one or more of those keys to + continue to access this account (like if one of them is your laptop or + your home desktop etc.) then instead of deleting the line you can just + delete everything upto but not including the words "ssh-rsa" or "ssh-dss". + + * Now remove (or move aside or rename to something else if you're paranoid) + the following files and directories. + + ~/.gitolite + ~/.gitolite.rc + ~/repositories/gitolite-admin.git + + * Then remove all the `update` hooks that git installs on each repository. + The easiest way is: + + find ~/repositories -wholename "*.git/hooks/update" | xargs rm -f + + but you can do it manually if you want to be careful. + + * Finally, any remote users that still have access must update their clone's + remote URLs (edit `.git/config` in the repo) to prefix `repositories/` + before the actual path used, in order for the remote to still work. This + is because you'll now be accessing it through plain ssh, which means you + have to give it the full path. From 16cea9bf8c7c040db559dd2d8888e8dfede37af5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 18 Feb 2010 06:10:08 +0530 Subject: [PATCH 264/850] compile: move checking of reponame/repopatt/username out of expand_list let expand_list be just that "expand a list", and leave checking to be done outside. otherwise, commit 690604d79 has the side effect of restricting refs to $REPOPATT_PATT, and so for instance barfing on the perfectly valid RW+ refs/(?!heads/master) = alice bob (thanks to Teemu for catching this) --- src/gl-compile-conf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 371e912..842a30a 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -133,8 +133,6 @@ sub expand_list for my $item (@list) { - die "$ABRT bad user or repo name $item\n" - unless ($GL_WILDREPOS ? $item =~ $REPOPATT_PATT : $item =~ $REPONAME_PATT) or $item =~ $USERNAME_PATT; if ($item =~ /^@/) # nested group { die "$ABRT undefined group $item\n" unless $groups{$item}; @@ -198,6 +196,7 @@ sub parse_conf_file @repos = keys %repos; } else { @repos = expand_list ( @repos ); + do { die "$ABRT bad reponame $_\n" unless ($GL_WILDREPOS ? $_ =~ $REPOPATT_PATT : $_ =~ $REPONAME_PATT) } for @repos; } s/\bCREAT[EO]R\b/\$creater/g for @repos; } From e7ac085d61ad558b0a53cc68a20e8a859af91773 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Wed, 10 Feb 2010 22:00:43 +0200 Subject: [PATCH 265/850] List also non-wildcard repos in expand_wild List also all matching and accessible non-wildcard repositories in ssh expand command. Signed-off-by: Teemu Matilainen --- src/gitolite.pm | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 6f8025c..b87e45b 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -306,7 +306,7 @@ sub expand_wild # get the list of repo patterns &parse_acl($GL_CONF_COMPILED, "", "NOBODY", "NOBODY", "NOBODY"); - my %reponames = map { $_ => 1 } grep { $_ =~ $REPONAME_PATT } sort keys %repos; + my %normal_repos = %repos; # display matching repos (from *all* the repos in the system) that $user # has at least "R" access to @@ -316,24 +316,28 @@ sub expand_wild chomp ($actual_repo); $actual_repo =~ s/^\.\///; $actual_repo =~ s/\.git$//; - # actual_repo should not be present "as is" in the config, because if - # it does, those permissions will override anything inherited from a - # wildcard that also happens to match, and it would be misleading to - # show that here - next if $reponames{$actual_repo}; - # it has to match the pattern being expanded + # actual_repo has to match the pattern being expanded next unless $actual_repo =~ /^$repo$/; - - # find the creater and subsitute in repos - my ($creater, $read, $write) = &repo_rights($repo_base_abs, $actual_repo, $user); - # get access list with this - &parse_acl($GL_CONF_COMPILED, $actual_repo, $creater, $read || "NOBODY", $write || "NOBODY"); - - my $perm = " "; - $perm .= ($repos{$actual_repo}{R}{'@all'} or $repos{$actual_repo}{R}{$user}) ? " R" : " "; - $perm .= ($repos{$actual_repo}{W}{'@all'} or $repos{$actual_repo}{W}{$user}) ? " W" : " "; - next if $perm eq " "; - print "$perm\t($creater)\t$actual_repo\n"; + # if actual_repo is present "as is" in the config, those + # permissions will override anything inherited from a + # wildcard that also happens to match + my $creater; + if ($normal_repos{$actual_repo}) { + %repos = %normal_repos; + $creater = ''; + } else { + # find the creater and subsitute in repos + my ($read, $write); + ($creater, $read, $write) = &repo_rights($repo_base_abs, $actual_repo, $user); + # get access list with this + &parse_acl($GL_CONF_COMPILED, $actual_repo, $creater, $read || "NOBODY", $write || "NOBODY"); + $creater = "($creater)"; + } + my $perm = ' '; + $perm .= ( $repos{$actual_repo}{R}{'@all'} ? ' @' : ( $repos{$actual_repo}{R}{$user} ? ' R' : ' ' ) ); + $perm .= ( $repos{$actual_repo}{W}{'@all'} ? ' @' : ( $repos{$actual_repo}{W}{$user} ? ' W' : ' ' ) ); + next if $perm eq ' '; + print "$perm\t$creater\t$actual_repo\n"; } } From 1de9e963f0abf9d971689a88c6c646481a57b61a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 18 Feb 2010 19:20:46 +0530 Subject: [PATCH 266/850] auth: behave better when no argument supplied to wild commands expand gets a default '.*' argument others die with an error message --- src/gl-auth-command | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index f7f415a..db16113 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -103,7 +103,7 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { # get and set perms for actual repo created by wildcard-autoviv # ---------------------------------------------------------------------------- -my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\s/; +my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\b/; # note that all the subs called here chdir somewhere else and do not come # back; they all blithely take advantage of the fact that processing custom @@ -112,7 +112,9 @@ my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\s/; if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) { die "wildrepos disabled, sorry\n" unless $GL_WILDREPOS; my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; - my ($verb, $repo) = ($cmd =~ /^\s*(\S+)\s+\/?(.*?)(?:.git)?$/); + my ($verb, $repo) = ($cmd =~ /^\s*(\S+)(?:\s+\/?(.*?)(?:.git)?)?$/); + # deal with "no argument" cases + $verb eq 'expand' ? $repo = '.*' : die "$verb needs an argument\n" unless $repo; if ($repo =~ $REPONAME_PATT and $verb =~ /getperms|setperms/) { # with an actual reponame, you can "getperms" or "setperms" get_set_perms($repo_base_abs, $repo, $verb, $user); From d1d399f6b74e72a3796813c09a88159300461ae5 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Thu, 25 Feb 2010 04:27:51 +0200 Subject: [PATCH 267/850] contrib: Add info of Vim syntax highlight Grand opening of the "contrib" directory. =) Signed-off-by: Teemu Matilainen --- contrib/vim/README.mkd | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 contrib/vim/README.mkd diff --git a/contrib/vim/README.mkd b/contrib/vim/README.mkd new file mode 100644 index 0000000..ba6a000 --- /dev/null +++ b/contrib/vim/README.mkd @@ -0,0 +1,11 @@ +# Vim Syntax Highlight + +[Vim][] Syntax highlight for `gitolite.conf` can be found from: + +- [vim.org script page][vim.org] (Releases) +- [GitHub][] (Sources) + + +[Vim]: http://www.vim.org/ +[vim.org]: http://www.vim.org/scripts/script.php?script_id=2900 +[GitHub]: http://github.com/tmatilai/gitolite.vim From 802f925f1d739b5e296831f043953480c1552219 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 25 Feb 2010 20:13:16 +0530 Subject: [PATCH 268/850] doc/CHANGELOG added --- doc/CHANGELOG | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 doc/CHANGELOG diff --git a/doc/CHANGELOG b/doc/CHANGELOG new file mode 100644 index 0000000..2a648aa --- /dev/null +++ b/doc/CHANGELOG @@ -0,0 +1,39 @@ +Major changes to gitolite, master branch only, most recent first, no dates but +the tags can help you position stuff approximately + + - v1.1 + + - contrib directory added + - expand now lists non-wildcard repos also + - refs also have groups now + - allow admins to get "info" for other users + + - wildrepos merged + - getdesc and setdesc for wildrepos + - htpasswd subcommand + - access control for rsync + + - v1.0 + + - sshkeys-lint program added, doc/6 revamped + - @SHELL in config changed to $SHELL_USERS in rc + - "include" mechanism + - delegation now uses NAME/ instead of branches + - PATH/ changed to NAME/ + + - @SHELL in config + - use of @all for repos also (see doc for caveat) + - config entries for repos + + - deny rules (no more "rebel" branch!) + - PATH/ + - specify gitweb owner + + - v0.95 + - easy install can run from msysgit also + - v0.90 + - allow admin defined hooks + - specify gitweb desc + - v0.85 + - emergency addkey program + - v0.80 From 42cc720eaac6082e4efc71981427541f3a445488 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 26 Feb 2010 07:13:19 +0530 Subject: [PATCH 269/850] easy install: be more specific about NOT adding repos manually --- src/gl-easy-install | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 35c31e2..6586e87 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -583,7 +583,7 @@ it elsewhere later if you wish to. " tail=" -NOTE: All the below stuff is on your *workstation*. You shoud not, normally, +NOTE: All the below stuff is on your *workstation*. You should not, normally, have to do anything directly on your server to administer/use gitolite. The admin repo is currently cloned at ~/gitolite-admin. You can reclone it @@ -591,8 +591,9 @@ anywhere else if you wish. To administer gitolite, make changes to the config file (conf/gitolite.conf) and/or the pubkeys (in subdirectory 'keydir') in any clone, then git add, git commit, and git push. -ADDING REPOS: Edit the config file to give *some* user access to the repo. -When you push, an empty repo will be created on the server. +ADDING REPOS: Do NOT add repos manually on the server. Edit the config file +to give *some* user access to the repo. When you push, an empty repo will be +created on the server. ADDING USERS: copy their pubkey as keydir/.pub, add it, commit and push. From deda3da18271ab86e1d8be971159654d31a23885 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Fri, 26 Feb 2010 16:55:28 +0200 Subject: [PATCH 270/850] auth: do not anchor the pattern given for expand Currently the pattern of expand command is line anchored. This is different than in e.g. grep, and causes extra work to add '.*' prefix and/or suffix in many use cases. The new semantics now mean you might get more matches than you would have gotten earlier. However, the expand command is still totally undocumented, so I think it is acceptable to change the functionality. ;) This patch removes the anchoring. So for earlier behavior the specified pattern needs be in form of '^$'. The default pattern is also changed from '.*' to '^', so there might be even a small speed improvement. =) Signed-off-by: Teemu Matilainen --- src/gitolite.pm | 2 +- src/gl-auth-command | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 46d537c..c61a566 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -320,7 +320,7 @@ sub expand_wild $actual_repo =~ s/^\.\///; $actual_repo =~ s/\.git$//; # actual_repo has to match the pattern being expanded - next unless $actual_repo =~ /^$repo$/; + next unless $actual_repo =~ /$repo/; # if actual_repo is present "as is" in the config, those # permissions will override anything inherited from a # wildcard that also happens to match diff --git a/src/gl-auth-command b/src/gl-auth-command index 443bd8f..3d7eefc 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -115,7 +115,7 @@ if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) { my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; my ($verb, $repo) = ($cmd =~ /^\s*(\S+)(?:\s+\/?(.*?)(?:.git)?)?$/); # deal with "no argument" cases - $verb eq 'expand' ? $repo = '.*' : die "$verb needs an argument\n" unless $repo; + $verb eq 'expand' ? $repo = '^' : die "$verb needs an argument\n" unless $repo; if ($repo =~ $REPONAME_PATT and $verb =~ /getperms|setperms/) { # with an actual reponame, you can "getperms" or "setperms" get_set_perms($repo_base_abs, $repo, $verb, $user); From 4cf18d8339e7f3902158a4df98bbfd5cf9f77265 Mon Sep 17 00:00:00 2001 From: Eli Barzilay Date: Fri, 26 Feb 2010 15:11:36 -0500 Subject: [PATCH 271/850] make Emacs use perl mode --- conf/example.gitolite.rc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index f1e5b7e..0ecad64 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -195,4 +195,7 @@ $GL_WILDREPOS = 0; # per perl rules, this should be the last line in such a file: 1; +# Local variables: +# mode: perl +# End: # vim: set syn=perl: From 9f805646fe9b18d1830f69721eee470145799aa7 Mon Sep 17 00:00:00 2001 From: Eli Barzilay Date: Fri, 26 Feb 2010 15:11:38 -0500 Subject: [PATCH 272/850] minor typos --- conf/example.conf | 2 +- doc/3-faq-tips-etc.mkd | 2 +- doc/4-wildcard-repositories.mkd | 2 +- doc/6-ssh-troubleshooting.mkd | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index e6aac3b..de2ccf1 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -102,7 +102,7 @@ repo gitolite R = @staff RW+ = sitaram - # you can split up access rules for a repo as convenient + # you can split up access rules for a repo for convenience # (notice that @oss_repos contains gitolite also) repo @oss_repos R = @all diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 684086a..ef92ca8 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -107,7 +107,7 @@ to replicate it manually. The input is your pubkey, typically /home/git/.ssh /home/git -[Actually, sshd requires that even directories *above* ~ (/, /home, +[Actually, `sshd` requires that even directories *above* `~` (`/`, `/home`, typically) also must be `go-w`, but that needs root. And typically they're already set that way anyway. (Or if they're not, you've got bigger problems than gitolite install not working!)] diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index aba4f66..a0fc1dc 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -159,7 +159,7 @@ use 'getperms' to check: The following points are important: - * note the syntax of the commands; it's not a "git" command,and there's no + * note the syntax of the commands; it's not a "git" command, and there's no `:` like in a repo URL. The first space-separated word is R or RW, and the rest are simple usernames. diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 83d908b..976c6b6 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -22,7 +22,7 @@ gitolite *after* the install has completed successfully. In addition, I **strongly** recommend reading [this document][glb] -- it's a very detailed look at how gitolite uses ssh's features on the server side. -Most people don't know ssh as well as they *think* they do; even if you dont +Most people don't know ssh as well as they *think* they do; even if you don't have any problems right now, it's worth skimming over. In addition to both these documents, there's now a program called From 572a34740f521bc1d7ff651f0d510bfbe84503bb Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 27 Feb 2010 12:46:02 +0530 Subject: [PATCH 273/850] doc/0: emphasise the importance of ssh --- doc/0-INSTALL.mkd | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index bb22bb6..a0cf54b 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -9,7 +9,14 @@ if he chooses to. This document tells you how to install gitolite. After the install is done, you may want to see the [admin document][admin] for adding users, repos, etc. +**Please note** that gitolite depends heavily on proper ssh setup and pubkey +based access. Sadly, most people don't know ssh as well as they think they +do. To make matters worse, ssh problems in gitolite don't always look like +ssh problems. Please read about [ssh troubleshooting][doc6] if you have *any* +kind of trouble installing gitolite! + [admin]: http://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd +[doc6]: http://github.com/sitaramc/gitolite/blob/pu/doc/6-ssh-troubleshooting.mkd In this document: From 8031f72fa8fbd939b261f69735a6980c93ce9f06 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 27 Feb 2010 17:28:06 +0530 Subject: [PATCH 274/850] progit article added to doc/ --- doc/progit-article.mkd | 158 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 doc/progit-article.mkd diff --git a/doc/progit-article.mkd b/doc/progit-article.mkd new file mode 100644 index 0000000..4c2c193 --- /dev/null +++ b/doc/progit-article.mkd @@ -0,0 +1,158 @@ +## Gitolite ## + +Git has started to become very popular in corporate environments, which tend to have some additional requirements in terms of access control. Gitolite was created to help with those requirements. + +Gitolite allows you to specify permissions not just by repository (like Gitosis does), but also by branch or tag names within each repository. That is, you can specify that certain people (or groups of people) can only push certain "refs" (branches or tags) but not others. + +### Installing ### + +Installing Gitolite is very easy, even if you don't read the extensive documentation that comes with it. You need an account on a Unix server of some kind; various Linux flavours, and Solaris 10, have been tested. You do not need root access, assuming git, perl, and an openssh compatible ssh server are already installed. In the examples below, we will use the `gitolite` account on a host called `gitserver`. + +Curiously, Gitolite is installed by running a script *on the workstation*, so your workstation must have a bash shell available. Even the bash that comes with msysgit will do, in case you're wondering. + +You start by obtaining public key based access to your server, so that you can log in from your workstation to the server without getting a password prompt. The following method works on Linux; for other workstation OSs you may have to do this manually. We assume you already had a key pair generated using `ssh-keygen`. + + $ ssh-copy-id -i ~/.ssh/id_rsa gitolite@gitserver + +This will ask you for the password to the gitolite account, and then set up public key access. This is **essential** for the install script, so check to make sure you can run a command without getting a password prompt: + + $ ssh gitolite@gitserver pwd + /home/gitolite + +Next, you clone Gitolite from the project's main site and run the "easy install" script (the third argument is your name as you would like it to appear in the resulting gitolite-admin repository): + + $ git clone git://github.com/sitaramc/gitolite + $ cd gitolite/src + $ ./gl-easy-install -q gitolite gitserver sitaram + +And you're done! Gitolite has now been installed on the server, and you now have a brand new repository called `gitolite-admin` in the home directory of your workstation. You administer your gitolite setup by making changes to this repository and pushing (just like Gitosis). + +[By the way, *upgrading* gitolite is also done the same way. Also, if you're interested, run the script without any arguments to get a usage message.] + +That last command does produce a fair amount of output, which might be interesting to read. Also, the first time you run this, a new keypair is created; you will have to choose a passphrase or hit enter for none. Why a second keypair is needed, and how it is used, is explained in the "ssh troubleshooting" document that comes with Gitolite. (Hey the documentation has to be good for *something*!) + +### Customising the Install ### + +While the default, quick, install works for most people, there are some ways to customise the install if you need to. If you omit the `-q` argument, you get a "verbose" mode install -- detailed information on what the install is doing at each step. The verbose mode also allows you to change certain server-side parameters, such as the location of the actual repositories, by editing an "rc" file that the server uses. This "rc" file is liberally commented so you should be able to make any changes you need quite easily, save it, and continue. This file also contains various settings that you can change to enable or disable some of gitolite's advanced features. + +### Config File and Access Control Rules ### + +So once the install is done, you switch to the `gitolite-admin` repository (placed in your HOME directory) and poke around to see what you got: + + $ cd ~/gitolite-admin/ + $ ls + conf/ keydir/ + $ find conf keydir -type f + conf/gitolite.conf + keydir/sitaram.pub + $ cat conf/gitolite.conf + #gitolite conf + # please see conf/example.conf for details on syntax and features + + repo gitolite-admin + RW+ = sitaram + + repo testing + RW+ = @all + +Notice that "sitaram" (the last argument in the `gl-easy-install` command you gave earlier) has read-write permissions on the `gitolite-admin` repository as well as a public key file of the same name. + +The config file syntax for Gitolite is *quite* different from Gitosis. Again, this is liberally documented in `conf/example.conf`, so we'll only mention some highlights here. + +You can group users or repos for convenience. The group names are just like macros; when defining them, it doesn't even matter whether they are projects or users; that distinction is only made when you *use* the "macro". + + @oss_repos = linux perl rakudo git gitolite + @secret_repos = fenestra pear + + @admins = scott # Adams, not Chacon, sorry :) + @interns = ashok # get the spelling right, Scott! + @engineers = sitaram dilbert wally alice + @staff = @admins @engineers @interns + +You can control permissions at the "ref" level. In the following example, interns can only push the "int" branch. Engineers can push any branch whose name starts with "eng-", and tags that start with "rc" followed by a digit. And the admins can do anything (including rewind) to any ref. + + repo @oss_repos + RW int$ = @interns + RW eng- = @engineers + RW refs/tags/rc[0-9] = @engineers + RW+ = @admins + +The expression after the `RW` or `RW+` is a regular expression (regex) that the refname (ref) being pushed is matched against. So we call it a "refex"! Of course, a refex can be far more powerful than shown here, so don't overdo it if you're not comfortable with perl regexes. + +Also, as you probably guessed, Gitolite prefixes `refs/heads/` as a syntactic convenience if the refex does not begin with `refs/`. + +An important feature of the config file's syntax is that all the rules for a repository need not be in one place. You can keep all the common stuff together, like the rules for all `oss_repos` shown above, then add specific rules for specific cases later on, like so: + + repo gitolite + RW+ = sitaram + +That rule will just get added to the ruleset for the `gitolite` repository. + +At this point you might be wondering how the access control rules are actually applied, so let's go over that briefly. + +There are two levels of access control in gitolite. The first is at the repository level; if you have read (or write) access to *any* ref in the repository, then you have read (or write) access to the repository. This is the only access control that Gitosis had. + +The second level, applicable only to "write" access, is by branch or tag within a repository. The username, the access being attempted (`W` or `+`), and the refname being updated are known. The access rules are checked in order of appearance in the config file, looking for a match for this combination (but remember that the refname is regex-matched, not merely string-matched). If a match is found, the push succeeds. A fallthrough results in access being denied. + +### Advanced Access Control with "deny" rules ### + +So far, we've only seen permissions to be one or `R`, `RW`, or `RW+`. However, gitolite allows another permission: `-`, standing for "deny". This gives you a lot more power, at the expense of some complexity, because now fallthrough is not the *only* way for access to be denied, so the *order of the rules now matters*! + +Let us say, in the situation above, we want engineers to be able to rewind any branch *except* master and integ. Here's how to do that: + + RW master integ = @engineers + - master integ = @engineers + RW+ = @engineers + +Again, you simply follow the rules top down until you hit a match for your access mode, or a deny. Non-rewind push to master or integ is allowed by the first rule. A rewind push to those refs does not match the first rule, drops down to the second, and is therefore denied. Any push (rewind or non-rewind) to refs other than master or integ won't match the first two rules anyway, and the third rule allows it. + +### Restricting pushes by files changed ### + +In addition to restricting what branches a user can push changes to, you can also restrict what files they are allowed to touch. For example, perhaps the Makefile (or some other program) is really not supposed to be changed by just anyone, because a lot of things depend on it or would break if the changes are not done *just right*. You can tell gitolite: + + repo foo + RW = @junior_devs @senior_devs + + RW NAME/ = @senior_devs + - NAME/Makefile = @junior_devs + RW NAME/ = @junior_devs + +This powerful feature is documented in `conf/example.conf`. + +### Personal Branches ### + +Gitolite also has a feature called "personal branches" (or rather, "personal branch namespace") that can be very useful in a corporate environment. + +A lot of code exchange in the git world happens by "please pull" requests. In a corporate environment, however, unauthenticated access is a no-no, and a developer workstation cannot do authentication, so you have to push to the central server and ask someone to pull from there. + +This would normally cause the same branch name clutter as in a centralised VCS, plus setting up permissions for this becomes a chore for the admin. + +Gitolite lets you define a "personal" or "scratch" namespace prefix for each developer (for example, `refs/personal//*`), with full permissions for that dev only, and read access for everyone else. Just choose a verbose install and set the `$PERSONAL` variable in the "rc" file to `refs/personal`. That's all; it's pretty much fire and forget as far as the admin is concerned, even if there is constant churn in the project team composition. + +### "Wildcard" repositories ### + +Gitolite allows you to specify repositories with wildcards (actually perl regexes), like, for example `assignments/s[0-9][0-9]/a[0-9][0-9]`, to pick a random example. This is a *very* powerful feature, which has to be enabled by setting `$GL_WILDREPOS = 1;` in the rc file. It allows you to assign a new permission mode ("C") which allows users to create repositories based on such wild cards, automatically assigns ownership to the specific user who created it, allows him/her to hand out R and RW permissions to other users to collaborate, etc. This feature is documented in `doc/4-wildcard-repositories.mkd`. + +### Other Features ### + +We'll round off this discussion with a bunch of other features, all of which are described in great detail in the "faqs, tips, etc" document. + +**Logging**: Gitolite logs all successful accesses. If you were somewhat relaxed about giving people rewind permissions (`RW+`) and some kid blew away "master", the log file is a life saver, in terms of easily and quickly finding the SHA that got hosed. + +**Git outside normal PATH**: One extremely useful convenience feature in gitolite is support for git installed outside the normal `$PATH` (this is more common than you think; some corporate environments or even some hosting providers refuse to install things system-wide and you end up putting them in your own directories). Normally, you are forced to make the *client-side* git aware of this non-standard location of the git binaries in some way. With gitolite, just choose a verbose install and set `$GIT_PATH` in the "rc" files. No client-side changes are required after that :-) + +**Access rights reporting**: Another convenient feature is what happens when you try and just ssh to the server. Older versions of gitolite used to complain about the `SSH_ORIGINAL_COMMAND` environment variable being empty (see the ssh documentation if interested). Now Gitolite comes up with something like this: + + hello sitaram, the gitolite version here is v0.90-9-g91e1e9f + you have the following permissions: + R anu-wsd + R entrans + R W git-notes + R W gitolite + R W gitolite-admin + R indic_web_input + R shreelipi_converter + +**Delegation**: For really large installations, you can delegate responsibility for groups of repositories to various people and have them manage those pieces independently. This reduces the load on the main admin, and makes him less of a bottleneck. This feature has its own documentation file in the `doc/` directory. + +**Gitweb support**: Gitolite supports gitweb in several ways. You can specify which repos are visible via gitweb. You can set the "owner" and "description" for gitweb from the gitolite config file. Gitweb has a mechanism for you to implement access control based on HTTP authentication, so you can make it use the "compiled" config file that gitolite produces, which means the same access control rules (for read access) apply for gitweb and gitolite. From 6dbaa0d325fadd05eade40656d52e968c0ef0e6c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 1 Mar 2010 20:32:54 +0530 Subject: [PATCH 275/850] auth: expand etc. *may* have single-quotes around reponame --- src/gl-auth-command | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 3d7eefc..a21167b 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -113,7 +113,7 @@ my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\b/; if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) { die "wildrepos disabled, sorry\n" unless $GL_WILDREPOS; my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; - my ($verb, $repo) = ($cmd =~ /^\s*(\S+)(?:\s+\/?(.*?)(?:.git)?)?$/); + my ($verb, $repo) = ($cmd =~ /^\s*(\S+)(?:\s+'?\/?(.*?)(?:\.git)?'?)?$/); # deal with "no argument" cases $verb eq 'expand' ? $repo = '^' : die "$verb needs an argument\n" unless $repo; if ($repo =~ $REPONAME_PATT and $verb =~ /getperms|setperms/) { From be92feca6d903f092afab4a6cf3fb66fd35a5ca9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 2 Mar 2010 05:39:19 +0530 Subject: [PATCH 276/850] add conf/VERSION to .gitignore conf/VERSION is programmatically created, not manually, so you shouldn't be checking it in, which means it looks cleaner to explicitly put it in .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f60da1e..6316699 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.tgz *.tar.gz *.tar.bz2 +conf/VERSION From de0ecd04310b29eaf9cbdf28509523ec5ee53a59 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 7 Mar 2010 19:05:56 +0530 Subject: [PATCH 277/850] compile: make it easier to move repos into gitolite when repos are copied over from elsewhere, one had to run easy install once again to make the new (OS-copied) repo contain the proper update hook. We eliminate this step now, using a new, empty, "hook" as a sentinel and having "compile" check/fix all repos' hooks. Since you have to add the repos to conf anyway, this makes it as seamless as possible. The correct sequence now is - (server) copy the repo at the OS level - (admin clone) add it to conf/gitolite.conf, commit, push --- doc/2-admin.mkd | 20 ++++++++++++++++++++ hooks/common/gitolite-hooked | 0 src/gl-compile-conf | 13 ++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 hooks/common/gitolite-hooked diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index a93df4b..ad9f18e 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -7,6 +7,7 @@ In this document: * administer * adding users and repos + * moving pre-existing repos into gitolite * specifying gitweb and daemon access * custom hooks * custom git config @@ -40,6 +41,25 @@ Please read on to see how to do this correctly. * when done, commit your changes and push +#### moving pre-existing repos into gitolite + +One simple way to add a pre-existing repo to gitolite is to let gitolite +create it as a brand new repo as in the previous section, and then, from an +existing clone, "push --all" to the new one. + +However, if you have many existing repos to add, this can be time-consuming +and error-prone. Here's how to take a bunch of existing repos and add them to +gitolite: + + * make sure they're *bare* repos ;-) + + * log on to the server and copy the repos to `$REPO_BASE` (which defaults to + `~/repositories`), making sure that the directory names end in ".git". + + * back on your workstation, add each repo (without the `.git` suffix) to + `conf/gitolite.conf` in your gitolite-admin repo clone. Then add, commit, + push. + #### specifying gitweb and daemon access This is a feature that I personally do not use (corporate environments don't diff --git a/hooks/common/gitolite-hooked b/hooks/common/gitolite-hooked new file mode 100644 index 0000000..e69de29 diff --git a/src/gl-compile-conf b/src/gl-compile-conf index bd2780f..c9b953b 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -61,7 +61,7 @@ $Data::Dumper::Sortkeys = sub { return [ reverse sort keys %{$_[0]} ]; }; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $SHELL_USERS, $GL_WILDREPOS, $GL_GITCONFIG_KEYS); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $SHELL_USERS, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -400,6 +400,17 @@ for my $repo (sort keys %repos) { # new_repo would have chdir'd us away; come back wrap_chdir("$repo_base_abs"); } + + # when repos are copied over from elsewhere, one had to run easy install + # once again to make the new (OS-copied) repo contain the proper update + # hook. Perhaps we can make this easier now, and eliminate the easy + # install, with a quick check (and a new, empty, "hook" as a sentinel) + unless (-l "$repo.git/hooks/gitolite-hooked") { + ln_sf("$GL_ADMINDIR/hooks/common", "*", "$repo.git/hooks"); + # in case of package install, GL_ADMINDIR is no longer the top cop; + # override with the package hooks + ln_sf("$GL_PACKAGE_HOOKS/common", "*", "$repo.git/hooks") if $GL_PACKAGE_HOOKS; + } } warn "\n\t\t***** WARNING *****\n" . From 08811fa9c2fef044edc68121cf75635814360714 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 7 Mar 2010 19:31:21 +0530 Subject: [PATCH 278/850] easy install: update ending message when non-std ssh port used --- src/gl-easy-install | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gl-easy-install b/src/gl-easy-install index 05dd5dc..d12f98a 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -567,8 +567,11 @@ done! Reminder: *Your* URL for cloning any repo on this server will be gitolite:reponame.git + *Other* users you set up will have to use \$user@\$host:reponame.git + However, if your server uses a non-standard ssh port, they should use + ssh://\$user@\$host:\$port/reponame.git If this is your first time installing gitolite, please also: tail -31 \$0 From 369ff45d9236932b0b420bb6ff79cf57a27c5586 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 9 Mar 2010 21:57:26 +0530 Subject: [PATCH 279/850] easy install seemed to out of the GIT_PATH loop for some reason, I apparently did not test easy install with a non-standard path! Fixed... --- src/gl-easy-install | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index d12f98a..90a4061 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -336,9 +336,10 @@ run_install() { prompt "installing/upgrading..." "$v_ignore_stuff" - # extract the GL_ADMINDIR and REPO_BASE locations + # extract the GL_ADMINDIR, REPO_BASE and GIT_PATH locations GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") + GIT_PATH=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GIT_PATH'") # determine if this is an upgrade; we decide based on whether a file # called $GL_ADMINDIR/conf/gitolite.conf exists on the remote side. We @@ -409,6 +410,7 @@ setup_pta() { # space around the "=" in the second and third lines. echo "cd $REPO_BASE/gitolite-admin.git +PATH=$PATH:$GIT_PATH GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start " | ssh -p $port $user@$host From 4b7d144971e9201061f3e7a7cfe7e2367eb81544 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 9 Mar 2010 22:07:57 +0530 Subject: [PATCH 280/850] easy install: suppress that misleading "fatal" get rid of the "fatal: No HEAD commit to compare with (yet)" message --- src/gl-easy-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 90a4061..75b7017 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -412,7 +412,7 @@ setup_pta() { echo "cd $REPO_BASE/gitolite-admin.git PATH=$PATH:$GIT_PATH GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir -GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start +GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet 2>/dev/null || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start " | ssh -p $port $user@$host # MANUAL: now that the admin repo is created, you have to set the hooks From b3945d44c9c0f70054aebb3edc7b488c0544210b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 10 Mar 2010 06:24:53 +0530 Subject: [PATCH 281/850] docs and .gitattributes hadn't been updated for the change in hooks dir --- .gitattributes | 7 ++++--- conf/example.conf | 4 ++-- doc/2-admin.mkd | 10 +++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.gitattributes b/.gitattributes index 5d894ad..85f7e74 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ -conf/* crlf=input -src/* crlf=input -src/hooks/* crlf=input +conf/* crlf=input +src/* crlf=input +hooks/common/* crlf=input +hooks/gitolite-admin/* crlf=input diff --git a/conf/example.conf b/conf/example.conf index de2ccf1..c197dbc 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -253,8 +253,8 @@ gitolite "Sitaram Chamarty" = "fast, secure, access control for git in a corpora # syntax: # config sectionname.keyname = [optional value_string] -# example usage: if you placed a hook in src/hooks that requires configuration -# information that is specific to each repo, you could do this: +# example usage: if you placed a hook in hooks/common that requires +# configuration information that is specific to each repo, you could do this: repo gitolite config hooks.mailinglist = gitolite-commits@example.tld diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index ad9f18e..b5a6c08 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -99,15 +99,15 @@ for the special usernames or remove the description line. #### custom hooks If you want to put in your own, custom, hooks every time a new repo is created -by gitolite, put a **tested** hook script in `src/hooks`. As distributed, the -only file there is the `update` hook, but everything (*everything*) in that -directory will get copied to the `hooks/` subdirectory of every *new* repo -created. +by gitolite, put a **tested** hook script in `hooks/common` of your gitolite +clone before running easy-install. As distributed, there are only two files +there, but everything (*everything*) in that directory will get copied to the +`hooks/` subdirectory of every *new* repo created. In order to push a new or updated hook script to *existing* repos as well, just run easy install once again; it'll do it to existing repos also. -**VERY IMPORTANT SECURITY NOTE: the `update` hook in `src/hooks` is what +**VERY IMPORTANT SECURITY NOTE: the `update` hook in `hooks/common` is what implements all the branch-level permissions in gitolite. If you fiddle with the hooks directory, please make sure you do not mess with this file accidentally, or all your fancy per-branch permissions will stop working.** From 7588c8cf541f3557b0bde8d8bde24a42a68b4953 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 12 Mar 2010 09:04:00 +0530 Subject: [PATCH 282/850] dps: gl-setup may have to create ~/.ssh and touch the authkeys file... I've been unwilling to create the authkeys file if it does not already exist, because it represents a significant change in accessibility for that account. However, in the "distro package" scenario, one wants to make it as easy as possible for the end-user (who is actually an admin for the gitolite being hosted on his account, let's not forget) to use. And it seems that in some cases that might mean he does not (yet) have a ~/.ssh even... --- src/gl-setup | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/gl-setup b/src/gl-setup index 25e4e0f..4f9ccd1 100755 --- a/src/gl-setup +++ b/src/gl-setup @@ -52,6 +52,15 @@ else cp $GL_PACKAGE_CONF/example.gitolite.rc ~/.gitolite.rc fi +# setup ssh stuff. We break our normal rule that we will not fiddle with +# authkeys etc., because in this case it seems appropriate +cd +mkdir -p .ssh +touch .ssh/authorized_keys +chmod go-w . .ssh .ssh/authorized_keys + +# now we get to gitolite itself + gl-install -q GL_ADMINDIR=$(cd;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR') From d660822ab5d1e2245eb1bca208117b2f2ebc784a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 12 Mar 2010 10:24:53 +0530 Subject: [PATCH 283/850] dps: made dps section clearer and more step-by-step --- doc/0-INSTALL.mkd | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index a0cf54b..15f9532 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -284,33 +284,50 @@ can be, say, `/usr/share/gitolite/conf` or some such, and similarly location "Y" can be perhaps `/usr/share/gitolite/hooks`. It's upto your distro policies where they are. -These are the content changes needed (no trailing slashes in the location -values please): +**Step 1**: Clone the gitolite repo and run the make command inside the clone - * `gl-setup` should have the following line: + git clone git://github.com/sitaramc/gitolite.git + cd gitolite + make pu.tar # or "make master.tar" or "make v1.2.tar" etc + +Then you explode the tar file in some temporary location. + +*Alternatively, you can `git checkout` the tag or branch you want, and run +this command in the clone directly*: + + git describe --tags --long > conf/VERSION + +**Step 2**: Now make the following changes (no trailing slashes in the +location values please): + + * `src/gl-setup` should have the following line: GL_PACKAGE_CONF="X" - * `example.gitolite.rc` should have the following lines: + * `conf/example.gitolite.rc` should have the following lines: $GL_PACKAGE_CONF="X"; $GL_PACKAGE_HOOKS="Y"; -This is where the files should be installed: + * delete `src/gl-easy-install`; that script is meant for a totally different + mode of installation and does *not* play well in this mode :-) + +**Step 3**: Move (or arrange to move) the files to their proper locations as +given below: * everything in "src" goes somewhere on the PATH * everything in "conf" goes to location "X" * everything in "hooks" goes to location "Y" -You might also want to delete the `gl-easy-install` script, since that is -meant for a totally different mode of installation and probably would *not* -work if a user tried to run it :-) +**Step 4**: There is no step 4. Unless you count telling your users to run +`gl-setup` as a step :) -On the initial install, you could also choose to setup a userid called -"gitolite", and run "gl-setup" as that user; however I do not know how you -would come up with the initial pubkey that is needed. Anyway, the point is -that the "gitolite" user is no more special than any other in terms of hosting -gitolite. Any user can host it by just running "gl-setup". +On the initial install (urpmi, yum install, or apt-get install), you could +also choose to setup a userid called "gitolite", and run "gl-setup" as that +user; however I do not know how you would come up with the initial pubkey that +is needed. Anyway, the point is that the "gitolite" user is no more special +than any other in terms of hosting gitolite. Any user can host gitolite on +his userid by just running "gl-setup". When you upgrade, just overwrite all the files; it'll all just work. In fact, other than the initial "gl-setup" run, the only time a gitolite hosting user From 367e8f893264d325a428b12accef078fb181bd2f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 12 Mar 2010 11:08:51 +0530 Subject: [PATCH 284/850] minor LFCR -> CRLF fix --- src/gitolite.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index c61a566..fbebeb3 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -286,12 +286,12 @@ sub report_basic # send back some useful info if no command was given print "hello $user, the gitolite version here is "; system("cat", ($GL_PACKAGE_CONF || "$GL_ADMINDIR/conf") . "/VERSION"); - print "\ryou have the following permissions:\n\r"; + print "\ryou have the following permissions:\r\n"; for my $r (sort keys %repos) { my $perm .= ( $repos{$r}{C}{'@all'} ? ' @' : ( $repos{$r}{C}{$user} ? ' C' : ' ' ) ); $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{$r}{R}{$user} ? ' R' : ' ' ) ); $perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ) ); - print "$perm\t$r\n\r" if $perm =~ /\S/; + print "$perm\t$r\r\n" if $perm =~ /\S/; } } @@ -358,7 +358,7 @@ sub special_cmd # check each special command we know about and call it if enabled if ($cmd eq 'info') { &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); - print "you also have shell access\n\r" if $shell_allowed; + print "you also have shell access\r\n" if $shell_allowed; } elsif ($cmd =~ /^info\s+(.+)$/) { my @otherusers = split ' ', $1; &parse_acl($GL_CONF_COMPILED); From bf7aba7e0b080cbad5cde84c78d0abd27ed37a4b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 12 Mar 2010 17:20:12 +0530 Subject: [PATCH 285/850] changelog --- doc/CHANGELOG | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 2a648aa..4dd89b7 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -1,6 +1,15 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately + - v1.3 + + - easier to move repos into gitolite + - pattern for expand is no longer anchored + + - v1.2 + + - distro packaging support -- easy to install systemwide now + - v1.1 - contrib directory added From ed5c78349e9e5f46ccff4e96d29ae41a75c3fc32 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 14 Mar 2010 22:07:34 +0530 Subject: [PATCH 286/850] update hook now allows chaining to "update.secondary" the changes to cp/scp are because without "-p" they dont carry perms across to existing files. So if you forgot to chmod +x your custom hook and ran easy install, then after that you have to go to the server side to fix the perms... --- doc/2-admin.mkd | 12 ++++++++++++ hooks/common/update | 7 +++++++ src/gl-easy-install | 2 +- src/gl-install | 6 +++--- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index b5a6c08..c615fb8 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -10,6 +10,7 @@ In this document: * moving pre-existing repos into gitolite * specifying gitweb and daemon access * custom hooks + * when you need your own "update" hook * custom git config ### administer @@ -112,6 +113,17 @@ implements all the branch-level permissions in gitolite. If you fiddle with the hooks directory, please make sure you do not mess with this file accidentally, or all your fancy per-branch permissions will stop working.** +#### when you need your own "update" hook + +Gitolite basically takes over the update hook for all repos, but some setups +really need the update hook functionality for their own purposes too. In +order to allow this, Gitolite now exec's a hook called `update.secondary` when +it's own "update" hook is done and everything is ready to go. + +You can create this `update.secondary` hook selectively on some repos on the +server, or use the mechanism in the previous section to make gitolite install +it on *all* your repos. + #### custom git config The custom hooks feature is a blunt instrument -- all repos get the hook you diff --git a/hooks/common/update b/hooks/common/update index 62f839b..3423522 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -54,6 +54,8 @@ require "$ENV{GL_BINDIR}/gitolite.pm"; # start... # ---------------------------------------------------------------------------- +my @saved_ARGV = @ARGV; # for chaining to another update hook at the end + my $ref = shift; my $oldsha = shift; my $newsha = shift; @@ -110,4 +112,9 @@ my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, (shift @refs), $perm); &log_it("$ENV{GL_TS} $perm\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . "\t$reported_repo\t$ref\t$ENV{GL_USER}\t$log_refex\n"); + +# now chain to the local admin defined update hook, if present +exec "./hooks/update.secondary", @saved_ARGV + if -f "hooks/update.secondary" or -l "hooks/update.secondary"; + exit 0; diff --git a/src/gl-easy-install b/src/gl-easy-install index 75b7017..c4dfdb1 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -282,7 +282,7 @@ copy_gl() { # have to create the directory first. ssh -p $port $user@$host mkdir -p gitolite-install - scp $quiet -P $port -r src conf doc hooks $user@$host:gitolite-install/ + scp $quiet -P $port -p -r src conf doc hooks $user@$host:gitolite-install/ # MANUAL: now log on to the server (ssh git@server) and get a command # line. This step is for your convenience; the script does it all from diff --git a/src/gl-install b/src/gl-install index b163439..f24a0de 100755 --- a/src/gl-install +++ b/src/gl-install @@ -59,10 +59,10 @@ for my $dir qw(conf doc keydir logs src hooks hooks/common hooks/gitolite-admin) # "src" and "doc" will be overwritten on each install, but not conf if ($GL_PACKAGE_HOOKS) { - system("cp -R $GL_PACKAGE_HOOKS $GL_ADMINDIR"); + system("cp -R -p $GL_PACKAGE_HOOKS $GL_ADMINDIR"); } else { - system("cp -R $bindir/../src $bindir/../doc $bindir/../hooks $GL_ADMINDIR"); - system("cp $bindir/../conf/VERSION $GL_ADMINDIR/conf"); + system("cp -R -p $bindir/../src $bindir/../doc $bindir/../hooks $GL_ADMINDIR"); + system("cp $bindir/../conf/VERSION $GL_ADMINDIR/conf"); } unless (-f $GL_CONF or $GL_PACKAGE_CONF) { From 83884aa75820ec075ce4120c2c672482eda57368 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 16 Mar 2010 07:26:33 +0530 Subject: [PATCH 287/850] compile/update hook: enable new style personal branches The new style personal branches work by interpreting the special sequence /USER/ (including the slashes) in a refname. Docs should be in the next commit... --- hooks/common/update | 1 + src/gitolite.pm | 1 + src/gl-compile-conf | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hooks/common/update b/hooks/common/update index 3423522..44f494e 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -39,6 +39,7 @@ die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; our $creater = $ENV{GL_CREATER}; our $readers = $ENV{GL_READERS}; our $writers = $ENV{GL_WRITERS}; + our $gl_user = $ENV{GL_USER}; die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; diff --git a/src/gitolite.pm b/src/gitolite.pm index fbebeb3..65709fa 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -250,6 +250,7 @@ sub parse_acl our $creater = $ENV{GL_CREATER} = $c || $ENV{GL_CREATER} || "NOBODY"; our $readers = $ENV{GL_READERS} = $r || $ENV{GL_READERS} || "NOBODY"; our $writers = $ENV{GL_WRITERS} = $w || $ENV{GL_WRITERS} || "NOBODY"; + our $gl_user = $ENV{GL_USER}; die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; diff --git a/src/gl-compile-conf b/src/gl-compile-conf index c9b953b..1693dcc 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -217,6 +217,7 @@ sub parse_conf_file # fully qualify refs that dont start with "refs/" or "NAME/"; # prefix them with "refs/heads/" @refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs; + @refs = map { s(/USER/)(/\$gl_user/); $_ } @refs; # expand the user list, unless it is just "@all" @users = expand_list ( @users ) @@ -367,7 +368,7 @@ my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); # the dump uses single quotes, but we convert any strings containing $creater, # $readers, $writers, to double quoted strings. A wee bit sneaky, but not too # much... -$dumped_data =~ s/'(?=[^']*\$(?:creater|readers|writers))(.*?)'/"$1"/g; +$dumped_data =~ s/'(?=[^']*\$(?:creater|readers|writers|gl_user))(.*?)'/"$1"/g; print $compiled_fh $dumped_data; close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; From 2456cc17c866e9a0de79982b7d2ff55868c1aa72 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 16 Mar 2010 10:37:33 +0530 Subject: [PATCH 288/850] personal branches: de-emphasise old-style, document new-style There are some disadvantages to the old-style personal branch scheme. It only allows one specific pattern (of refname) to be used, forces that pattern to be applicable to *all* repos in the entire config, and requires editing the rc file (on the server) to be edited to achieve this. In other words, it's a very blunt instrument... The new style depends on using lines like this within a specific repo config: RW+ personal/USER/ = @userlist The important thing is that the "branch" name should contain `/USER/` (including the slashes). Access is still determined by the right hand side of course. This gives you the following advantages: - allow it only for repos that need it - allow different patterns to be used for different repos - allow *multiple* patterns; just add more than one such line - allow the pattern to have suffixes (eg: foo/USER/bar) --- conf/example.gitolite.rc | 20 ---------------- doc/3-faq-tips-etc.mkd | 52 ++++++++-------------------------------- doc/progit-article.mkd | 2 +- 3 files changed, 11 insertions(+), 63 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 0ecad64..e7df2dd 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -65,26 +65,6 @@ $GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm"; # -------------------------------------- -# personal branch prefix; leave it as is (empty) if you don't want to use the -# feature (see the "developer-specific branches" section in the "faq, tips, -# etc" document) - -$PERSONAL=""; - -# uncomment one of these if you do want it. If you change it, remember it -# MUST start with "refs/" - -# I recommend this: -# $PERSONAL="refs/personal"; - -# if you want something more visible/noisy, use this: -# $PERSONAL="refs/heads/personal"; - -# NOTE: whatever value you choose, for security reasons it is better to make -# it fully qualified -- that is, starting with "refs/" - -# -------------------------------------- - # if git on your server is on a standard path (that is # ssh git@server git --version # works), leave this setting as is. Otherwise, choose one of the diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index ef92ca8..faa3312 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -461,51 +461,19 @@ authentication, even work shared just between 2 devs has to go *via* the server. This causes the same branch name clutter as in a centralised VCS, plus setting up permissions for this becomes a chore for the admin. -gitolite lets you define a "personal" or "scratch" namespace prefix for -each developer (e.g., `refs/personal//*`), with full -permissions for that dev and read-only for everyone else. And you get -this without adding a single line to the access config file -- pretty -much fire and forget as far as the admin is concerned, even if there is -constant churn in the project teams. +gitolite lets you define a "personal" or "scratch" namespace prefix for each +developer (e.g., `refs/personal//*`). Just add a line like: -Not bad for something that took just *one* line of code to implement. -And that's one clean, readable, line, by the way ;-) + RW+ personal/USER/ = @userlist -The admin would set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate -this to all users. It could be something like `refs/heads/personal`, which -means all such branches will show up in `git branch` lookups and `git clone` -will fetch them. Or he could use, say, `refs/personal`, which means it won't -show up in any normal "branch-y" commands and stuff, and generally be much -less noisy. +This means I (user "sitaram") can do anything to any branch whose name starts +with `personal/sitaram/` assuming I'm in "userlist". -**Note that a user who has NO write access cannot have personal branches**; if -you read the section (above) on "two levels of access rights checking" you'll -understand why. - -For instance, in the following example, `user3` cannot push to any -`refs/heads/personal/user3/*` branches because the first level check stops him -cold: - - # assume $PERSONAL = 'refs/heads/personal' in ~/.gitolite.rc - repo myrepo - RW+ master = sitaram - RW+ release = qa_guy - RW = user1 user2 - R = user3 - -If we relax that check, *any* access becomes *write* access. Yes it will be -caught later, by the hook, but it's good practice to catch things in multiple -places. - -If you want `user3` to have his own personal branch, but without write access -to any of the "real" branches (like "master", "release", etc.), just use a -dummy branch. Choose a name that will never exist in practice, or even if -someone creates it, we don't care. For example, this will get him past the -first check: - - RW dummy = user3 - -Just don't *show* the user this config file; it might sound insulting :-) +You can have any number of such lines with different prefixes (for example, +using topic names instead of "personal") or even suffixes if you like. The +important thing is that the "branch" name should contain `/USER/` (including +the slashes). At runtime this will match whoever is the current user. Access +is still determined by the right hand side of course. #### custom hooks and custom git config diff --git a/doc/progit-article.mkd b/doc/progit-article.mkd index 4c2c193..916faf6 100644 --- a/doc/progit-article.mkd +++ b/doc/progit-article.mkd @@ -127,7 +127,7 @@ A lot of code exchange in the git world happens by "please pull" requests. In a This would normally cause the same branch name clutter as in a centralised VCS, plus setting up permissions for this becomes a chore for the admin. -Gitolite lets you define a "personal" or "scratch" namespace prefix for each developer (for example, `refs/personal//*`), with full permissions for that dev only, and read access for everyone else. Just choose a verbose install and set the `$PERSONAL` variable in the "rc" file to `refs/personal`. That's all; it's pretty much fire and forget as far as the admin is concerned, even if there is constant churn in the project team composition. +Gitolite lets you define a "personal" or "scratch" namespace prefix for each developer (for example, `refs/personal//*`); see the "personal branches" section in `doc/3-faq-tips-etc.mkd` for details. ### "Wildcard" repositories ### From 33d6856f4bba168ac0c41135e36f369c7161ec89 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 16 Mar 2010 10:38:00 +0530 Subject: [PATCH 289/850] update: disallow old-style personal branches The downside is that the repo config does need to be edited and new style line(s) added. --- hooks/common/update | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hooks/common/update b/hooks/common/update index 44f494e..f8b5826 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -25,7 +25,7 @@ use warnings; # common definitions # ---------------------------------------------------------------------------- -our ($GL_CONF_COMPILED, $PERSONAL); +our ($GL_CONF_COMPILED); our %repos; # we should already have the GL_RC env var set when we enter this hook @@ -76,8 +76,6 @@ $perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); $perm = '+' if $oldsha ne $merge_base; my @allowed_refs; -# personal stuff -- right at the start in the new regime, I guess! -push @allowed_refs, { "$PERSONAL/$ENV{GL_USER}/" => "RW+" } if $PERSONAL; # we want specific perms to override @all, so they come first push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$ENV{GL_USER}} || [] }; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; From 05431233a2757d002f40052641dee11954a9f6cc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 16 Mar 2010 19:26:26 +0530 Subject: [PATCH 290/850] post-update hook now chains to post-update.secondary undocumented but analogous to the documented update hook chaining --- hooks/gitolite-admin/post-update | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hooks/gitolite-admin/post-update b/hooks/gitolite-admin/post-update index b84dfa8..752c212 100755 --- a/hooks/gitolite-admin/post-update +++ b/hooks/gitolite-admin/post-update @@ -6,3 +6,8 @@ GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master cd $GL_ADMINDIR $GL_BINDIR/gl-compile-conf + +if [[ -f hooks/post-update.secondary ]] || [[ -L hooks/post-update.secondary ]] +then + exec hooks/post-update.secondary "$@" +fi From 412a691810f6fcd68d2ccbd04b2352fc00badcf7 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 17 Mar 2010 17:45:49 +0530 Subject: [PATCH 291/850] compile: remove the sortsub for data dumper Data dumper was failing (returning an empty string!) on an input config file of about 350 lines or so (output 2400 lines or so). Removing the sort sub fixed the problem. To recap why that sub was put in (see deleted lines in this commit for details), what we really want is that $creater must appear *last* in the resulting dump. So we trick it. "man ascii" tells you that ~ is the highest valued ASCII character (yes, I know, not utf-8 safe etc... I'll deal with that if and when needed or punt!). So we just put that in front of $creater and remove it later... You *don't* want to do this for $readers and $writers -- then they will once again sort *after* $creater, which would be a bad thing. Also, it's probably better this way, because now the order of the hash keys will be: $readers, $writers, any actual users listed, and then $creater. This means the effective access rights will be: 1. if you are the creater you get CREATER's rights 2. else if your userid is listed *explicitly* in the config, you get those rights 3. else if you've been setperm'd as a writer, you get WRITERS rights 4. else if you've been setperm'd as a reader, you get READERS rights This is different from what used to happen till now; READERS and WRITERS used to trump explicitly given rights. I'd been meaning to fix that somehow, but never got around to it, until this DDD (damn Data Dumper!) forced my hand :) --- src/gitolite.pm | 5 ----- src/gl-compile-conf | 13 ++----------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 65709fa..061e57c 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -234,11 +234,6 @@ sub parse_acl my ($GL_CONF_COMPILED, $repo, $c, $r, $w) = @_; $c = $r = $w = "NOBODY" unless $GL_WILDREPOS; - # void $r if same as $w (otherwise "readers" overrides "writers"; this is - # the same problem that needed a sort sub for the Dumper in the compile - # script, but in this case it's limited to just $readers and $writers) - $r = "NOBODY" if $r eq $w; - # set up the variables for a parse to interpolate stuff from the dumped # hash (remember the selective conversion of single to double quotes?). diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 1693dcc..b308529 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -5,15 +5,6 @@ use warnings; use Data::Dumper; $Data::Dumper::Indent = 1; $Data::Dumper::Sortkeys = 1; -$Data::Dumper::Sortkeys = sub { return [ reverse sort keys %{$_[0]} ]; }; - # this is to make sure that $creater etc go to the end of the dumped hash. - # Without this, a setup that has something like - # @team = u1 u2 u3 - # repo priv/CREATER/.+ - # RW+ = CREATER - # RW = @team - # has a problem. The RW overrides the RW+ when the dumped hash is read in - # (simply going by sequence), so creater's special privs are lost # === add-auth-keys === @@ -224,7 +215,7 @@ sub parse_conf_file unless (@users == 1 and $users[0] eq '@all'); do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; - s/\bCREAT[EO]R\b/\$creater/g for @users; + s/\bCREAT[EO]R\b/~\$creater/g for @users; s/\bREADERS\b/\$readers/g for @users; s/\bWRITERS\b/\$writers/g for @users; @@ -368,7 +359,7 @@ my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); # the dump uses single quotes, but we convert any strings containing $creater, # $readers, $writers, to double quoted strings. A wee bit sneaky, but not too # much... -$dumped_data =~ s/'(?=[^']*\$(?:creater|readers|writers|gl_user))(.*?)'/"$1"/g; +$dumped_data =~ s/'(?=[^']*\$(?:creater|readers|writers|gl_user))~?(.*?)'/"$1"/g; print $compiled_fh $dumped_data; close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; From e91e8c80d9d38fa38b72c02eb691471f0a6f2d36 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 17 Mar 2010 20:36:51 +0530 Subject: [PATCH 292/850] minor oopsie in post-update hook chaining --- hooks/gitolite-admin/post-update | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hooks/gitolite-admin/post-update b/hooks/gitolite-admin/post-update index 752c212..2230efc 100755 --- a/hooks/gitolite-admin/post-update +++ b/hooks/gitolite-admin/post-update @@ -7,6 +7,8 @@ GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master cd $GL_ADMINDIR $GL_BINDIR/gl-compile-conf +cd - + if [[ -f hooks/post-update.secondary ]] || [[ -L hooks/post-update.secondary ]] then exec hooks/post-update.secondary "$@" From bfc9c7aeb59b3d2ff9d34f170763d08c96fa8598 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 17 Mar 2010 20:42:19 +0530 Subject: [PATCH 293/850] minor fixup; spurious error killed --- src/gl-compile-conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index b308529..4409e44 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -538,7 +538,7 @@ for my $pubkey (glob("*")) # lint check 3; a little more severe than the first two I guess... for my $user (sort keys %user_list) { - next if $user =~ /^(gitweb|daemon|\@all|\$creater|\$readers|\$writers)$/ or $user_list{$user} eq 'has pubkey'; + next if $user =~ /^(gitweb|daemon|\@all|~\$creater|\$readers|\$writers)$/ or $user_list{$user} eq 'has pubkey'; print STDERR "$WARN user $user in config, but has no pubkey!\n"; } From b537a4acd46177cb0027384807693c0d82effb1b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 18 Mar 2010 09:11:48 +0530 Subject: [PATCH 294/850] dash it all! Ubuntu now defaults to /bin/sh -> /bin/dash, while my brain seems to default to bash. I guess it's easier to fix my brain, and my code --- hooks/gitolite-admin/post-update | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/gitolite-admin/post-update b/hooks/gitolite-admin/post-update index 2230efc..5758cc9 100755 --- a/hooks/gitolite-admin/post-update +++ b/hooks/gitolite-admin/post-update @@ -9,7 +9,7 @@ $GL_BINDIR/gl-compile-conf cd - -if [[ -f hooks/post-update.secondary ]] || [[ -L hooks/post-update.secondary ]] +if [ -f hooks/post-update.secondary ] || [ -L hooks/post-update.secondary ] then exec hooks/post-update.secondary "$@" fi From f282b8f926e794c77ba22fa9c2e797d9606675dc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 18 Mar 2010 20:48:29 +0530 Subject: [PATCH 295/850] gl-setup: dash-compat before someone runs it on the new Ubuntu :) --- src/gl-setup | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/gl-setup b/src/gl-setup index 4f9ccd1..0dd8c84 100755 --- a/src/gl-setup +++ b/src/gl-setup @@ -19,23 +19,23 @@ GL_PACKAGE_CONF=/tmp/share/gitolite/conf # pubkey file if you happen to have lost all gitolite-access to the repos (but # do have shell access via some other means) -die() { echo "$@"; echo death at line number ${BASH_LINENO[0]}; exit 1; } +die() { echo "$@"; exit 1; } pubkey_file=$1 admin_name= -if [[ -n $pubkey_file ]] +if [ -n "$pubkey_file" ] then - [[ $pubkey_file =~ .pub$ ]] || die "$pubkey_file must end in .pub" - [[ -f $pubkey_file ]] || die "cant find $pubkey_file" + echo $pubkey_file | grep '.pub$' >/dev/null || die "$pubkey_file must end in .pub" + [ -f $pubkey_file ] || die "cant find $pubkey_file" admin_name=$(basename $pubkey_file .pub) fi -if [[ -f ~/.gitolite.rc ]] +if [ -f ~/.gitolite.rc ] then perl -ne 's/^\s+//; s/[\s=].*//; print if /^\$/;' < $GL_PACKAGE_CONF/example.gitolite.rc | sort > .newvars perl -ne 's/^\s+//; s/[\s=].*//; print if /^\$/;' < ~/.gitolite.rc | sort > .oldvars comm -23 .newvars .oldvars > .diffvars - if [[ -s .diffvars ]] + if [ -s .diffvars ] then cp $GL_PACKAGE_CONF/example.gitolite.rc ~/.gitolite.rc.new echo new version of the rc file saved in ~/.gitolite.rc.new @@ -48,7 +48,7 @@ then fi rm -f .newvars .oldvars .diffvars else - [[ -n $pubkey_file ]] || die "looks like first run -- I need a pubkey file" + [ -n "$pubkey_file" ] || die "looks like first run -- I need a pubkey file" cp $GL_PACKAGE_CONF/example.gitolite.rc ~/.gitolite.rc fi @@ -66,7 +66,7 @@ gl-install -q GL_ADMINDIR=$(cd;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR') REPO_BASE=$( cd;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ) -[[ -f $GL_ADMINDIR/conf/gitolite.conf ]] || { +[ -f $GL_ADMINDIR/conf/gitolite.conf ] || { cat < $GL_ADMINDIR/conf/gitolite.conf repo gitolite-admin RW+ = $admin_name @@ -75,7 +75,7 @@ REPO_BASE=$( cd;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ) RW+ = @all EOF } -[[ -n $pubkey_file ]] && cp $pubkey_file $GL_ADMINDIR/keydir +[ -n "$pubkey_file" ] && cp $pubkey_file $GL_ADMINDIR/keydir touch $HOME/.ssh/authorized_keys gl-compile-conf -q From bad0723974195331b85e9993b3d541cb0a23da39 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 18 Mar 2010 22:04:22 +0530 Subject: [PATCH 296/850] allow @all to be used as a "user" in setperms --- src/gitolite.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 061e57c..9c108a5 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -174,8 +174,8 @@ sub repo_rights my $fh = wrap_open("<", "$repo_base_abs/$repo.git/gl-perms"); my $perms = join ("", <$fh>); if ($perms) { - $r = $user if $perms =~ /^\s*R(?=\s).*\s$user(\s|$)/m; - $w = $user if $perms =~ /^\s*RW(?=\s).*\s$user(\s|$)/m; + $r = $user if $perms =~ /^\s*R(?=\s).*\s(\@all|$user)(\s|$)/m; + $w = $user if $perms =~ /^\s*RW(?=\s).*\s(\@all|$user)(\s|$)/m; } } From 364a2317c293c38007b7b0a95da3e97067e92209 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 19 Mar 2010 07:17:44 +0530 Subject: [PATCH 297/850] created gitolite@googlegroups.com, updated README enough people told me they want one because they don't watch the git ML or it's too high traffic, so I finally made one and invited a few folks. --- README.mkd | 1 + 1 file changed, 1 insertion(+) diff --git a/README.mkd b/README.mkd index 12596a4..ff653ed 100644 --- a/README.mkd +++ b/README.mkd @@ -108,3 +108,4 @@ details, looking for the word "security". Gitolite is released under GPL v2. See COPYING for details. sitaramc@gmail.com +mailing list: gitolite@googlegroups.com From b3c5d1442155fc473f413b3fe4f6e297fb7e01ae Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 20 Mar 2010 09:59:07 +0530 Subject: [PATCH 298/850] relent a little and document the expand command a tiny bit :) --- doc/4-wildcard-repositories.mkd | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index a0fc1dc..68dbaa0 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -184,7 +184,16 @@ allowed to *create* repos matching that pattern. In addition, there is also the "expand" command, which takes any regex pattern and returns you a list of all wildcard-created repos that you have access to -which fit that pattern. +which fit that pattern. And if, as an administrator, you wish to list out +*every single* repo that your users have created, add this to the *end* of the +config file: + + repo @all + R = sitaram # or whoever you are + +Push the config, then try + + ssh gitolite expand ### other issues and discussion From a3f1258a0a0c6b4604d827f98baa5c09dbc431db Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 23 Mar 2010 14:59:33 +0530 Subject: [PATCH 299/850] reduce a bit of code duplication in check_access; make it call check_ref --- src/gitolite.pm | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 9c108a5..979ca51 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -394,13 +394,7 @@ sub check_access push @allowed_refs, @ { $repos{$repo}{$ENV{GL_USER}} || [] }; push @allowed_refs, @ { $repos{$repo}{'@all'} || [] }; - for my $ar (@allowed_refs) { - my $refex = (keys %$ar)[0]; - next unless $ref =~ /^$refex/; - die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; - return if ($ar->{$refex} =~ /\Q$perm/); - } - die "$perm $ref $ENV{GL_REPO} $ENV{GL_USER} DENIED by fallthru\n"; + &check_ref(\@allowed_refs, $repo, $ref, $perm); } # ---------------------------------------------------------------------------- From 7bfb3676b76551ba3c611a2baaf6a43968bc19bb Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 23 Mar 2010 22:20:34 +0530 Subject: [PATCH 300/850] @all for repos is now much cleaner; a true @all... - no need to put it at the end of the config file now, yeaaay! - @all for @all is meaningless and not supported. People asking will be told to get a life or use git-daemon. - NAME/ limits for @all repos is ignored for efficiency reasons. --- conf/example.conf | 7 ++----- doc/3-faq-tips-etc.mkd | 14 +++++--------- doc/4-wildcard-repositories.mkd | 4 ++-- hooks/common/update | 4 +++- src/gitolite.pm | 20 ++++++++++++++------ src/gl-auth-command | 1 + src/gl-compile-conf | 4 +--- 7 files changed, 28 insertions(+), 26 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index c197dbc..8ba4919 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -107,11 +107,8 @@ repo gitolite repo @oss_repos R = @all - # set permissions to all already defined repos - # (a repository is defined if it has permission rules - # associated, empty "repo" stanza or "@group=..." line is - # not enough). *Please* do see doc/3-faq-tips-etc.mkd for - # some important notes on this feature + # set permissions to all repos. *Please* do see + # doc/3-faq-tips-etc.mkd for notes on this feature repo @all RW+ = @admins diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index faa3312..01ae889 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -126,16 +126,12 @@ seem to hurt anything. [Update 2009-09-14; this has been fixed in git ### `@all` syntax for repos There *is* a way to use the `@all` syntax for repos also, as described in -`conf/example.conf`. However, there is an important difference between this -and the old `@all` (for users): +`conf/example.conf`. However, there are a couple of minor cautions: - * `@all` for repos is immediately expanded, when found, into the currently - known list of repos. "Currently" means upto this point in the config - file, and "known" means having some user with some permissions associated - with the repo! - - * This means that if you really want *all* repos, you'd better put this para - at the **end** of the config file! + * don't use `NAME/` or such restrictions on the special `@all` repo. Due to + the potential for defeating a crucial optimisation and slowing down *all* + access, we do not support this. + * don't try giving `@all` users some permission for `@all` repos ### umask setting diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 68dbaa0..a70f239 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -185,8 +185,8 @@ allowed to *create* repos matching that pattern. In addition, there is also the "expand" command, which takes any regex pattern and returns you a list of all wildcard-created repos that you have access to which fit that pattern. And if, as an administrator, you wish to list out -*every single* repo that your users have created, add this to the *end* of the -config file: +*every single* repo that your users have created, add this to your config +file: repo @all R = sitaram # or whoever you are diff --git a/hooks/common/update b/hooks/common/update index f8b5826..d8f610c 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -76,8 +76,9 @@ $perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); $perm = '+' if $oldsha ne $merge_base; my @allowed_refs; -# we want specific perms to override @all, so they come first +# @all repos: see comments in similar code in check_access push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$ENV{GL_USER}} || [] }; +push @allowed_refs, @ { $repos{'@all'} {$ENV{GL_USER}} || [] }; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; # prepare the list of refs to be checked @@ -88,6 +89,7 @@ push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; # been specified my @refs = ($ref); # the first ref to check is the real one +# because making it work screws up efficiency like no tomorrow... if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) { # this is special to git -- the hash of an empty tree my $empty='4b825dc642cb6eb9a060e54bf8d69288fbee4904'; diff --git a/src/gitolite.pm b/src/gitolite.pm index 979ca51..e398b09 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -284,9 +284,13 @@ sub report_basic system("cat", ($GL_PACKAGE_CONF || "$GL_ADMINDIR/conf") . "/VERSION"); print "\ryou have the following permissions:\r\n"; for my $r (sort keys %repos) { - my $perm .= ( $repos{$r}{C}{'@all'} ? ' @' : ( $repos{$r}{C}{$user} ? ' C' : ' ' ) ); - $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{$r}{R}{$user} ? ' R' : ' ' ) ); - $perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ) ); + # @all repos; meaning of read/write flags: + # @ => @all users are allowed access to this repo + # r/w => you are allowed access to @all repos + # R/W => you are allowed access to this repo + my $perm .= ( $repos{$r}{C}{'@all'} ? ' @' : ( $repos{$r}{C}{$user} ? ' C' : ' ' ) ); + $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{'@all'}{R}{$user} ? ' r' : ( $repos{$r}{R}{$user} ? ' R' : ' ' ))); + $perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{'@all'}{W}{$user} ? ' w' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ))); print "$perm\t$r\r\n" if $perm =~ /\S/; } } @@ -333,8 +337,9 @@ sub expand_wild $creater = "($creater)"; } my $perm = ' '; - $perm .= ( $repos{$actual_repo}{R}{'@all'} ? ' @' : ( $repos{$actual_repo}{R}{$user} ? ' R' : ' ' ) ); - $perm .= ( $repos{$actual_repo}{W}{'@all'} ? ' @' : ( $repos{$actual_repo}{W}{$user} ? ' W' : ' ' ) ); + # @all repos; see notes above + $perm .= ( $repos{$actual_repo}{R}{'@all'} ? ' @' : ( $repos{'@all'}{R}{$user} ? ' r' : ( $repos{$actual_repo}{R}{$user} ? ' R' : ' ' ))); + $perm .= ( $repos{$actual_repo}{W}{'@all'} ? ' @' : ( $repos{'@all'}{W}{$user} ? ' w' : ( $repos{$actual_repo}{W}{$user} ? ' W' : ' ' ))); next if $perm eq ' '; print "$perm\t$creater\t$actual_repo\n"; } @@ -390,8 +395,11 @@ sub check_access # bit, sadly), this code duplicates stuff in the current update hook. my @allowed_refs; - # we want specific perms to override @all, so they come first + # user+repo specific perms override everything else, so they come first. + # Then perms given to specific user for @all repos, and finally perms + # given to @all users for specific repo push @allowed_refs, @ { $repos{$repo}{$ENV{GL_USER}} || [] }; + push @allowed_refs, @ { $repos{'@all'}{$ENV{GL_USER}} || [] }; push @allowed_refs, @ { $repos{$repo}{'@all'} || [] }; &check_ref(\@allowed_refs, $repo, $ref, $perm); diff --git a/src/gl-auth-command b/src/gl-auth-command index a21167b..df04167 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -188,6 +188,7 @@ my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W'); die "$perm access for $repo DENIED to $user\n" unless $repos{$repo}{$perm}{$user} + or $repos{'@all'}{$perm}{$user} # new: access to @all repos or $repos{$repo}{$perm}{'@all'}; # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 4409e44..ed74722 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -184,9 +184,7 @@ sub parse_conf_file { # grab the list and expand any @stuff in it @repos = split ' ', $1; - if (@repos == 1 and $repos[0] eq '@all') { - @repos = keys %repos; - } else { + unless (@repos == 1 and $repos[0] eq '@all') { @repos = expand_list ( @repos ); do { die "$ABRT bad reponame $_\n" unless ($GL_WILDREPOS ? $_ =~ $REPOPATT_PATT : $_ =~ $REPONAME_PATT) } for @repos; } From 6e17c74abff6101b68e11c9d04af612ca9088a4c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 26 Mar 2010 21:29:26 +0530 Subject: [PATCH 301/850] silly little PATH bug... what this means is that until now, everyone who used easy-install (without needing to set $GIT_PATH in the rc file) had a client-side PATH that was perfectly valid on the server side also! --- src/gl-easy-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index c4dfdb1..7a38f13 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -410,7 +410,7 @@ setup_pta() { # space around the "=" in the second and third lines. echo "cd $REPO_BASE/gitolite-admin.git -PATH=$PATH:$GIT_PATH +PATH=\$PATH:$GIT_PATH GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet 2>/dev/null || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start " | ssh -p $port $user@$host From a45d2d99128fcddf9f153312dfd1a7abc6f4044b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 27 Mar 2010 22:31:10 +0530 Subject: [PATCH 302/850] auth: do not implicitly assign RW access for creaters a configuration like this: repo CREATER/.* C = CREATER RW+ = WRITERS was buggy; CREATER was implicitly part of WRITERS so he got RW permissions implicitly, so the push went through --- src/gl-auth-command | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index df04167..0d618e9 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -173,7 +173,7 @@ if ( -d "$repo_base_abs/$repo.git" ) { my ($creater, $user_R, $user_W) = &repo_rights($repo_base_abs, $repo, $user); &parse_acl($GL_CONF_COMPILED, $repo, $creater, $user_R, $user_W); } else { - &parse_acl($GL_CONF_COMPILED, $repo, $user, $user, $user); + &parse_acl($GL_CONF_COMPILED, $repo, $user, "NOBODY", "NOBODY"); # auto-vivify new repo if you have C access (and wildrepos is on) if ( $GL_WILDREPOS and $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'} ) { From 6a44c564a2685d69cc2ed80547ce671a29df141d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 28 Mar 2010 12:30:02 +0530 Subject: [PATCH 303/850] doc/4: added "how it actually works" section thanks to Ilari for helping fix a bug (see previous commit) and then prompting this documentation --- doc/4-wildcard-repositories.mkd | 79 +++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index a70f239..3717aca 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -28,6 +28,7 @@ In this document: * setting a gitweb description for a wildcard-matched repo * reporting * other issues and discussion + * how it actually works This document is mostly "by example". @@ -61,15 +62,7 @@ new repo, as user "u4" (a student): Initialized empty Git repository in /home/gitolite/repositories/assignments/u4/a12.git/ warning: You appear to have cloned an empty repository. -Notice the *two* empty repo inits, and the order in which they occur ;-) Now -make some changes and push, and after that, that specific repo -(`assignments/u4/a12`) behaves as if the access control looked like this: - - # effective config - repo assignments/u4/a12 - RW+ = u4 - RW = WRITERS @TAs - R = READERS @prof +Notice the *two* empty repo inits, and the order in which they occur ;-) #### Wildcard repos without creater name in them @@ -215,6 +208,74 @@ Push the config, then try of the time, it won't be difficult; the fixed prefix will usually be different anyway so there won't be overlaps. +### how it actually works + +This section tells you what is happening inside gitolite so you can understand +this feature better. Let's use the config example at the beginning of this +document: + + repo assignments/CREATER/a[0-9][0-9] + C = @students + RW+ = CREATER + RW = WRITERS @TAs + R = READERS @prof + +First we find the set of rules to apply. This involves replacing the special +words CREATER, WRITERS, and READERS with appropriate usernames to derive an +"effective" ruleset for the repo in question. + +For a **new** repo, replace the word CREATER in all repo patterns and rules +with the name of the invoking user. + +> (Note: this is why you should never use `C = CREATER`; it becomes `C = +> invoking_user`! Unless you really want to allow *all* users to create +> repos, you should restrict "C" perms to an actual user or set of users, +> like in the examples in this document). + +For an **existing** repo, do the same but replace with the name of the user +who actually *created* the repo (this name is recorded in a special file in +the repo directory when the repo is first created, so it is available). + +Now find a repo pattern that matches the actual reponame being pushed -- this +tells you which set of rules to apply. Only one pattern is allowed to match; +matching more than one is an error. + +If the invoking user has been given "RW" permissions using `setperms`, all +occurrences of the word WRITERS are replaced by the invoking username. +Otherwise -- and this includes the "new repo" case, since you couldn't have +run `setperms` on a non-existant repo -- they are replaced by "NOBODY". + +(The same thing is done with "R" access and the word "READERS".) + +At this point we have an effective ruleset, and the normal access rules (R, +RW, etc) apply, with the addition that the invoking user needs "C" access to +be able to create a repo. + +> (Note: "C" rights do not automatically give the CREATER any other rights; +> they must be specifically given. `RW+ = CREATER` is recommended in most +> situations, as you can see in our example). + +Assuming user "u4" trying to push-create a new repo called +`assignments/u4/a23`, this is what the effective ruleset looks like (we're +ignoring the "NOBODY" business): + + repo assignments/u4/a23 + C = @students + RW+ = u4 + RW = @TAs + R = @prof + +If u4 gives "RW" perms to u5 using `setperms`, and u5 tries to access that +repo, the ruleset looks like: + + repo assignments/u4/a23 + C = @students + RW+ = u4 + RW = u5 @TAs + R = @prof + +I hope that helps. + ---- Enjoy, and please use with care. This is pretty powerful stuff. As they say: From 72b63abaf2407e0f566c1872d82706c7aac64c96 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 28 Mar 2010 23:44:37 +0530 Subject: [PATCH 304/850] auth, gitolite.pm: do not leak info about repo existence All this is about a user trying to look if a repo exists or not, when he does not have any access to that repo. Ideally, "repo does not exist" should be indistinguishable from "you dont have perms to that repo". (1) if $GL_WILDREPOS is not set, you either get a permissions error, or a "$repo not found in compiled config" death. Fixed. (2) if $GL_WILDREPOS is set, you either get either a permissions error, or a "$repo has no matches" death. Fixed. (3) The following combination leaks info about repo existence: - actual repo doesn't exist - spying user don't have C perms - repo patt doesn't contain CREATER - RW+ = CREATER is specified (as is normal) In such case, the "convenience copy" of the ACL that parse_acl makes, coupled with substituting CREATER for the invoking user means $repos{$actual_repo} has RW+ for the spying user. This means the access denied doesn't happen, and control passes to git, which promptly expresses it unhappiness and angst over being given a repo that 'does not appear to be a git repository' This doesn't happen if all those conditions are not met: - if repo exists, CREATER is set to the real creater, so RW+ = CREATER does not gain spying user anything - if spying user has C perms it just gets created, because he has rights. This is also info leak but we can't prevent it; tighten the config (maybe by including CREATER in repo pattern) if this is not wanted - if repo patt contains CREATER it will never match someone else's repo anyway! --- src/gitolite.pm | 18 ++++++++++++------ src/gl-auth-command | 4 ++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index e398b09..9850af8 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -253,15 +253,21 @@ sub parse_acl # want the config dumped as is, really return unless $repo; - return $ENV{GL_REPOPATT} = "" if $repos{$repo}; - # didn't find it, but wild is off? too bad, die!!! muahahaha - die "$repo not found in compiled config\n" unless $GL_WILDREPOS; + # return with "no wildcard match" status if you found the actual repo in + # the config or if wild is unset + return $ENV{GL_REPOPATT} = "" if $repos{$repo} or not $GL_WILDREPOS; - # didn't find $repo in %repos, so it must be a wildcard-match case + # didn't find actual repo in %repos, and wild is set, so find the repo + # pattern that matches the actual repo my @matched = grep { $repo =~ /^$_$/ } sort keys %repos; - die "$repo has no matches\n" unless @matched; + + # didn't find a match? avoid leaking info to user about repo existence; + # as before, pretend "no wildcard match" status + return $ENV{GL_REPOPATT} = "" unless @matched; + die "$repo has multiple matches\n@matched\n" if @matched > 1; - # found exactly one pattern that matched, copy its ACL + + # found exactly one pattern that matched, copy its ACL for convenience $repos{$repo} = $repos{$matched[0]}; # and return the pattern return $ENV{GL_REPOPATT} = $matched[0]; diff --git a/src/gl-auth-command b/src/gl-auth-command index 0d618e9..583cd4e 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -180,6 +180,10 @@ if ( -d "$repo_base_abs/$repo.git" ) { wrap_chdir("$repo_base_abs"); new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); wrap_chdir($ENV{HOME}); + } else { + # repo didn't exist, and you didn't have perms to create it. Delete + # the "convenience" copy of the ACL that parse_acl makes for us + delete $repos{$repo}; } } From 33b886c512cfe7ebd2281cdf7dd083c952ba7b04 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 30 Mar 2010 17:53:40 +0530 Subject: [PATCH 305/850] we're getting a nice solaris workout after a long time :) --- hooks/gitolite-admin/post-update | 3 ++- src/gl-setup | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/hooks/gitolite-admin/post-update b/hooks/gitolite-admin/post-update index 5758cc9..5edf96d 100755 --- a/hooks/gitolite-admin/post-update +++ b/hooks/gitolite-admin/post-update @@ -4,10 +4,11 @@ # (the GL_ADMINDIR env var would have been set by gl-auth-command) GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master +od=$PWD cd $GL_ADMINDIR $GL_BINDIR/gl-compile-conf -cd - +cd $od if [ -f hooks/post-update.secondary ] || [ -L hooks/post-update.secondary ] then diff --git a/src/gl-setup b/src/gl-setup index 0dd8c84..b31c6c5 100755 --- a/src/gl-setup +++ b/src/gl-setup @@ -27,20 +27,20 @@ if [ -n "$pubkey_file" ] then echo $pubkey_file | grep '.pub$' >/dev/null || die "$pubkey_file must end in .pub" [ -f $pubkey_file ] || die "cant find $pubkey_file" - admin_name=$(basename $pubkey_file .pub) + admin_name=` basename $pubkey_file .pub` fi -if [ -f ~/.gitolite.rc ] +if [ -f $HOME/.gitolite.rc ] then perl -ne 's/^\s+//; s/[\s=].*//; print if /^\$/;' < $GL_PACKAGE_CONF/example.gitolite.rc | sort > .newvars - perl -ne 's/^\s+//; s/[\s=].*//; print if /^\$/;' < ~/.gitolite.rc | sort > .oldvars + perl -ne 's/^\s+//; s/[\s=].*//; print if /^\$/;' < $HOME/.gitolite.rc | sort > .oldvars comm -23 .newvars .oldvars > .diffvars if [ -s .diffvars ] then - cp $GL_PACKAGE_CONF/example.gitolite.rc ~/.gitolite.rc.new - echo new version of the rc file saved in ~/.gitolite.rc.new + cp $GL_PACKAGE_CONF/example.gitolite.rc $HOME/.gitolite.rc.new + echo new version of the rc file saved in $HOME/.gitolite.rc.new echo - echo please update ~/.gitolite.rc manually if you need features + echo please update $HOME/.gitolite.rc manually if you need features echo controlled by any of the following variables: echo ---- sed -e 's/^/ /' < .diffvars @@ -49,7 +49,7 @@ then rm -f .newvars .oldvars .diffvars else [ -n "$pubkey_file" ] || die "looks like first run -- I need a pubkey file" - cp $GL_PACKAGE_CONF/example.gitolite.rc ~/.gitolite.rc + cp $GL_PACKAGE_CONF/example.gitolite.rc $HOME/.gitolite.rc fi # setup ssh stuff. We break our normal rule that we will not fiddle with @@ -63,8 +63,8 @@ chmod go-w . .ssh .ssh/authorized_keys gl-install -q -GL_ADMINDIR=$(cd;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR') -REPO_BASE=$( cd;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ) +GL_ADMINDIR=` cd;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'` +REPO_BASE=` cd;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ` [ -f $GL_ADMINDIR/conf/gitolite.conf ] || { cat < $GL_ADMINDIR/conf/gitolite.conf From 967af2c9938f21f396fcef3b874886dbb7795011 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 30 Mar 2010 20:01:49 +0530 Subject: [PATCH 306/850] compile/update: new "D" permission normally, RW+ means permission to rewind or delete. Now, if you use "D" permission anywhere in a repo config, that means "delete" and RW+ then means only "rewind", no delete. --- conf/example.conf | 14 ++++++-------- doc/3-faq-tips-etc.mkd | 23 +++++++++++++++++++++++ hooks/common/update | 4 ++++ src/gl-compile-conf | 9 +++++++-- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 8ba4919..6d8c3d7 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -60,11 +60,6 @@ # "@interns" now has 3 names in it, but note that this does # not change @staff -# WILDCARD REPOSITORIES ("wildrepos" BRANCH ONLY) -# ----------------------------------------- - -# Please see doc/4-wildcard-repositories.mkd for details - # REPO AND BRANCH PERMISSIONS # --------------------------- @@ -72,10 +67,13 @@ # start line: # repo [one or more repos and/or repo groups] # followed by one or more permissions lines: -# (R|RW|RW+) [zero or more refexes] = [one or more users] +# (C|D|R|RW|RW+) [zero or more refexes] = [one or more users] -# there are 3 types of permissions: R, RW, and RW+. The "+" means permission -# to "rewind" (force push a non-fast forward to) a branch +# there are 5 types of permissions: R, RW, and RW+ are simple (the "+" means +# permission to "rewind" -- force push a non-fast forward to -- a branch). +# The C permission is described in doc/4-wildcard-repositories.mkd. The D +# permission is described in doc/3-faq-tips-etc.mkd, in the "advanced +# features" section. # how permissions are matched: # - user, repo, and access (W or +) are known. For that combination, if diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 01ae889..982db8f 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -18,6 +18,7 @@ In this document: * two levels of access rights checking * better logging * "exclude" (or "deny") rules + * the "D" permission -- separating delete and rewind rights * file/dir NAME based restrictions * delegating parts of the config file * convenience features @@ -366,6 +367,28 @@ And here's how it works: before the third one, and it has a `-` as the permission, so the push fails +#### the "D" permission -- separating delete and rewind rights + +Since the beginning, `RW+` meant being able to rewind *or* delete a ref. My +stand is that these two are fairly similar, and infact a rewind is almost the +same as a delete+push (the only difference I can see is if you had +core.logAllRefUpdates set, which is *not* a default setting). + +However, there seem to be cases where it is useful to distinguish them -- +situations where one of them should be restricted more than the other. +([Arguments][sdrr] exist for both sides: restrict delete more than rewind, and +vice versa). + +So we now allow these two rights to be separated. Just use the new `D` +permission anywhere in the config for the repo, and instantly all `RW+` +permissions (for that repo) cease to permit deletion of the ref matched. + +This provides the *greatest* backward compatibility (if you don't specify any +`D` permissions, everything works just as before), while also enabling the new +semantics at the granularity of a repo, instead of the entire config. + +[sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c# + #### file/dir NAME based restrictions In addition to branch-name based restrictions, gitolite also allows you to diff --git a/hooks/common/update b/hooks/common/update index d8f610c..e1b1209 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -75,6 +75,10 @@ $perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); # notice that ref delete looks like a rewind, as it should $perm = '+' if $oldsha ne $merge_base; +# were any 'D' perms specified? If they were, it means we have to separate +# deletes from rewinds, so if the new sha is all 0's, change the '+' to a 'D' +$perm = 'D' if $repos{$ENV{GL_REPO}}{DELETE_IS_D} and $newsha eq '0' x 40; + my @allowed_refs; # @all repos: see comments in similar code in check_access push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$ENV{GL_USER}} || [] }; diff --git a/src/gl-compile-conf b/src/gl-compile-conf index ed74722..575ee61 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -191,7 +191,7 @@ sub parse_conf_file s/\bCREAT[EO]R\b/\$creater/g for @repos; } # actual permission line - elsif (/^(-|C|R|RW|RW\+) (.* )?= (.+)/) + elsif (/^(-|C|D|R|RW|RW\+) (.* )?= (.+)/) { my $perms = $1; my @refs; @refs = split(' ', $2) if $2; @@ -257,7 +257,12 @@ sub parse_conf_file # for 1st level check (see faq/tips doc) $repos{$repo}{C}{$user} = 1, next if $perms eq 'C'; $repos{$repo}{R}{$user} = 1 if $perms =~ /R/; - $repos{$repo}{W}{$user} = 1 if $perms =~ /W/; + $repos{$repo}{W}{$user} = 1 if $perms =~ /W|D/; + + # if the user specified even a single 'D' anywhere, make + # that fact easy to find; this changes the meaning of RW+ + # to no longer permit deletes (see update hook) + $repos{$repo}{DELETE_IS_D} = 1 if $perms eq 'D'; # for 2nd level check, store each "ref, perms" pair in order for my $ref (@refs) From 5aba13cd80f1d94a3a07af2e47c710dd8a2980dd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 31 Mar 2010 06:45:29 +0530 Subject: [PATCH 307/850] allow 'D' for @all repos ...so that the new semantics can be made system-default if someone wants to do that --- doc/3-faq-tips-etc.mkd | 13 +++++++++++++ hooks/common/update | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 982db8f..5939832 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -387,6 +387,19 @@ This provides the *greatest* backward compatibility (if you don't specify any `D` permissions, everything works just as before), while also enabling the new semantics at the granularity of a repo, instead of the entire config. +Note 1: if you find that `RW+` no longer allows deletion but you can't see a +`D` permission in the rules, remember that gitolite allows a repo config to be +specified in multiple places for convenience, included delegated or included +files. Be sure to search everywhere :) + +Note 2: a quick way to make this the default for *all* your repos is: + + repo @all + D dummy-branch = foo + +where foo can be either the administrator, or if you can ignore the warning +message when you push, a non-existant user. + [sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c# #### file/dir NAME based restrictions diff --git a/hooks/common/update b/hooks/common/update index e1b1209..3f85c2b 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -77,7 +77,7 @@ $perm = '+' if $oldsha ne $merge_base; # were any 'D' perms specified? If they were, it means we have to separate # deletes from rewinds, so if the new sha is all 0's, change the '+' to a 'D' -$perm = 'D' if $repos{$ENV{GL_REPO}}{DELETE_IS_D} and $newsha eq '0' x 40; +$perm = 'D' if ( $repos{$ENV{GL_REPO}}{DELETE_IS_D} or $repos{'@all'}{DELETE_IS_D} ) and $newsha eq '0' x 40; my @allowed_refs; # @all repos: see comments in similar code in check_access From e6ee5cdb301e75a1e19e4317138a139a1e68f46f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 31 Mar 2010 14:42:41 +0530 Subject: [PATCH 308/850] 4b7d144 should have touched this also --- src/gl-setup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-setup b/src/gl-setup index b31c6c5..b12d9fd 100755 --- a/src/gl-setup +++ b/src/gl-setup @@ -84,7 +84,7 @@ gl-compile-conf -q od=$PWD cd; cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir -GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start +GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet 2>/dev/null || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start cd $od # now that the admin repo is created, you have to set the hooks properly; best From 5deffee3cff5f9a13c59b8c1e357c5a32487d1c3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 9 Apr 2010 16:48:46 +0530 Subject: [PATCH 309/850] security: gitolite admin can get shell access by using screwy pubkey name example: keydir/sitaram@$(some-dangerous-command; echo hi).pub (still won't get the reward; that is only if a non-admin user gets privs!) --- src/gl-compile-conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 575ee61..1fb942b 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -511,6 +511,12 @@ print $newkeys_fh "# gitolite start\n"; wrap_chdir($GL_KEYDIR); for my $pubkey (glob("*")) { + # security check (thanks to divVerent for catching this) + unless ($pubkey =~ $USERNAME_PATT) { + print STDERR "$pubkey contains some unsavoury characters; ignored...\n"; + next; + } + # lint check 1 unless ($pubkey =~ /\.pub$/) { From 246165537d132fd429f83a21e70a47ad772c10a6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 9 Apr 2010 20:52:32 +0530 Subject: [PATCH 310/850] new server-side program "gl-tool", subcommand "shell-add" Previous implementations of "give shell access to some gitolite users" feature were crap. There was no easy/elegant way to ensure that someone who had repo admin access would not manage to get himself shell access. Giving someone shell access requires that you should have shell access in the first place, so the simplest way is to enable it from the server side only. So now that we decided to do that, we may as well prepare for other, future, commands by starting a server-side utility program with sub-commands (the only current one being "shell-add") --- conf/example.gitolite.rc | 13 ------ doc/6-ssh-troubleshooting.mkd | 37 +++++++----------- src/gl-compile-conf | 8 +--- src/gl-tool | 74 +++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 42 deletions(-) create mode 100755 src/gl-tool diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index e7df2dd..bda9d2d 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -75,19 +75,6 @@ $GIT_PATH=""; # -------------------------------------- -# if you want to give shell access to any gitolite user(s), name them here. -# Please see doc/6-ssh-troubleshooting.mkd for details on how this works. - -# Do not add people to this list indiscriminately. AUDITABILITY OF ACCESS -# CONTROL CHANGES (AND OF REPO ACCESSES) WILL BE COMPROMISED IF ADMINS CAN -# FIDDLE WITH THE ACTUAL (PLAIN TEXT) LOG FILES THAT GITOLITE KEEPS, WHICH -# THEY CAN EASILY DO IF THEY HAVE A SHELL. - -# syntax: space separated list of gitolite usernames in *one* string variable. -# $SHELL_USERS = "alice bob"; - -# -------------------------------------- - # ---------------------------------------------------------------------- diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 976c6b6..826a5ff 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -386,32 +386,23 @@ instance if you have *two* gitolite servers you are administering)? We've managed (thanks to an idea from Jesse Keating) to make it possible for a single key to allow both gitolite access *and* shell access. -This is done by: +This is done by copying the pubkey (to which you want to give shell access) to +the server and running either - * (**on the server**) listing all such users in a variable called - `$SHELL_USERS` in the `~/.gitolite.rc` file. For example: + cd $HOME/.gitolite # assuming default $GL_ADMINDIR in ~/.gitolite.rc + src/gl-tool shell-add ~/foo.pub - $SHELL_USERS = "alice bob"; +or - (Note the syntax: a space separated list of users in one string variable). + gl-tool shell-add ~/foo.pub - * (**on your client**) make at least a dummy change to your clone of the - gitolite-admin repo and push it. +The first method is to be used if you used the **user-install** mode, while +the second method is for the **system-install followed by user-setup** mode +(see doc/0-INSTALL.mkd, section on "install methods", for more on this) -**IMPORTANT UPGRADE NOTE**: a previous implementation of this feature worked -by adding people to a special group (`@SHELL`) in the *config* file. This -meant that anyone with gitolite-admin repo write access could add himself to -the `@SHELL` group and push, thus obtaining shell. - -This is not a problem for most setups, but if someone wants to separate these -two privileges (the right to push the admin repo and the right to get a shell) -then it does pose a problem. Since the "rc" file can only be edited by -someone who already has shell access, we now use that instead, even though -this forces a change in the syntax. - -To migrate from the old scheme to the new one, add a new variable -`$SHELL_USERS` to `~/.gitolite.rc` on the server with the appropriate names in -it. **It is best to do this directly on the server *before* upgrading to this -version.** (After the upgrade is done and tested you can remove the `@SHELL` -lines from the gitolite config file). +**IMPORTANT UPGRADE NOTE**: previous implementations of this feature were +crap. There was no easy/elegant way to ensure that someone who had repo admin +access would not manage to get himself shell access. +Giving someone shell access requires that you should have shell access in the +first place, so the simplest way is to enable it from the server side only. diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 1fb942b..786f19b 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $SHELL_USERS, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -537,11 +537,7 @@ for my $pubkey (glob("*")) print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n"; next; } - if ($SHELL_USERS and $SHELL_USERS =~ /(^|\s)$user(\s|$)/) { - print $newkeys_fh "command=\"$AUTH_COMMAND -s $user\",$AUTH_OPTIONS "; - } else { - print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS,no-pty "; - } + print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS,no-pty "; print $newkeys_fh $pubkey_content; } # lint check 3; a little more severe than the first two I guess... diff --git a/src/gl-tool b/src/gl-tool new file mode 100755 index 0000000..f6a201a --- /dev/null +++ b/src/gl-tool @@ -0,0 +1,74 @@ +#!/bin/sh + +# BEGIN USAGE + +# $0 -- make some server side tasks easier + +# Usage: +# $0 [sub-command [args]] + +# Security notes: this program does not do any sanitisation of input. You're +# running it at the CLI on the server, so you already have the power to do +# whatever you want anyway. + +# current sub-commands: + +# (1) REPLACE THE OLD $SHELL_USERS MECHANISM + +# $0 shell-add foo.pub +# adds the pubkey in foo.pub into the authkeys file with "-s" argument (shell +# access) and user "foo". The line will be added *before* the "# gitolite +# start" section, so that a gitolite-admin push will not affect it. + +# Although there is no "shell-remove" sub-command, you can do that quite +# easily by editing ~/.ssh/authorized_keys and deleting the appropriate line. + +# END USAGE + + +die() { echo "$@"; exit 1; } + +if [ -z "$1" ] +then + perl -ne 's/\$0/$ARGV/ge; print if /BEGIN USAGE/../END USAGE/' $0 | grep -v USAGE | cut -c3- + exit 1 +fi + +if [ "$1" = "shell-add" ] +then + # sanity checks + [ -z "$2" ] && exec $0 + [ -f "$2" ] || die "$2 does not exist" + wc -l < $2 | grep '^1$' >/dev/null || die "$2 contains more than one line" + + # must be kept consistent with what's in src/gl-compile-conf; on the plus + # side, it's not likely to change anytime soon! + AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding" + + bindir=`echo $0 | perl -lpe 's/^/$ENV{PWD}\// unless /^\//; s/\/[^\/]+$//;'` + + pubkey_file=$2 + user=`basename $pubkey_file .pub` + + authline="command=\"$bindir/gl-auth-command -s $user\",$AUTH_OPTIONS `cat $pubkey_file`"; + + authkeys=$HOME/.ssh/authorized_keys + + for i in 1 + do + perl -lne "last if /# gitolite start/; print unless /gl-auth-command -s $user/; " $authkeys + echo $authline + perl -lne "print if /# gitolite start/ .. 0; " $authkeys + done > $authkeys.new + + diff -u $authkeys $authkeys.new && die no change to authkey file + echo + echo If the above diff looks ok, press enter. Else press Ctrl-C. + read dummy + cat $authkeys > $authkeys.old + cat $authkeys.new > $authkeys + + exit 0 +fi + +die "could not understand command $1" From 67607760e5dd551e2fbfd24fa1114aa33a154894 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 9 Apr 2010 21:49:54 +0530 Subject: [PATCH 311/850] bypass update hook if GL_BYPASS_UPDATE_HOOK is available in ENV people with shell access should be allowed to bypass the update hook, to allow them to clone locally and push. You can now do this by setting an env var that the ssh "front door" will never set, like so: GL_BYPASS_UPDATE_HOOK=1 git push Note that this will NOT work for the gitolite-admin repo, because the post-update hook on that one requires a bit more. If you really want to do that, try: GL_ADMINDIR=~/.gitolite GL_BINDIR=~/.gitolite/src GL_BYPASS_UPDATE_HOOK=1 git push (assuming default values in ~/.gitolite.rc) --- hooks/common/update | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hooks/common/update b/hooks/common/update index 3f85c2b..2a3e3e6 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -28,6 +28,10 @@ use warnings; our ($GL_CONF_COMPILED); our %repos; +# people with shell access should be allowed to bypass the update hook, simply +# by setting an env var that the ssh "front door" will never set +exit 0 if exists $ENV{GL_BYPASS_UPDATE_HOOK}; + # we should already have the GL_RC env var set when we enter this hook die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; # then "do" the compiled config file, whose name we now know. Before doing From e0fe73ac18b06ed6bba06c8a1754a30c9a9da20c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 10 Apr 2010 08:58:17 +0530 Subject: [PATCH 312/850] compile: recurse through keydir/ for pubkeys --- src/gl-compile-conf | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 786f19b..ab4e3d6 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -509,10 +509,12 @@ while (<$authkeys_fh>) # options, in the standard ssh authorized_keys format), then the "end" line. print $newkeys_fh "# gitolite start\n"; wrap_chdir($GL_KEYDIR); -for my $pubkey (glob("*")) +for my $pubkey (`find . -type f`) { + chomp($pubkey); $pubkey =~ s(^\./)(); + # security check (thanks to divVerent for catching this) - unless ($pubkey =~ $USERNAME_PATT) { + unless ($pubkey =~ $REPONAME_PATT) { print STDERR "$pubkey contains some unsavoury characters; ignored...\n"; next; } @@ -523,7 +525,11 @@ for my $pubkey (glob("*")) print STDERR "WARNING: pubkey files should end with \".pub\", ignoring $pubkey\n"; next; } - my $user = $pubkey; $user =~ s/(\@[^.]+)?\.pub$//; + + my $user = $pubkey; + $user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub + $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz + # lint check 2 print STDERR "WARNING: pubkey $pubkey exists but user $user not in config\n" unless $user_list{$user}; From 4b65cc51d32e2b0def7a7ce71c1d0a4640a89255 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Apr 2010 04:09:50 +0530 Subject: [PATCH 313/850] document how to create multiple gitolite instances on one server... ...and provide a pointer from the delegations doc for people taking delegation too far ;-) --- doc/0-INSTALL.mkd | 23 +++++++++++++++++++++++ doc/5-delegation.mkd | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 15f9532..4351a46 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -28,6 +28,7 @@ In this document: * upgrades * other notes * system install / user setup + * multiple gitolite instances * next steps * appendix A: server and client requirements for user install * server @@ -165,6 +166,28 @@ the server and run the same command as above. `$GL_PACKAGE_CONF` and `$GL_PACKAGE_HOOKS`. If you remove or change either of them, expect trouble :-) +#### multiple gitolite instances + +With this mode of install, it's easy to create multiple gitolite instances +(say one for each department, on some mega company-wide server). You can even +do this without giving shell access to the admins. Here's an example with +just two "departments", and their admins Alice and Bob: + + * create userids `webbrowser_repos` and `webserver_repos` + * ask Alice and Bob for their pubkeys; copy them to the respective home + directories for convenience + * run `su - webbrowser_repos`, then `gl-setup alice.pub` + * (similarly with `webserver_repos` and `bob.pub`, and so on for others) + +That's it. The URL for all web browser projects is now something like +`webbrowser_repos@server:reponame`, and similarly for the others. + +Notice that you only have to do this once for each "department", and it's +really just one command after creating the userid. None of these admins need +to have a command line on the server, so don't give them the passwords if you +don't need to -- the pubkey will allow them to be gitolite admins on their +domain, and that's quite enough for normal operations. + ### next steps The last message produced by the easy install script should tell you how to diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index 9260046..e3403ca 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -97,3 +97,24 @@ Naturally, a successful push invokes the post-update hook that the admin repo has, which eventually runs the compile script. The **net effect** is as if you appended the contents of all the "fragment" files, in alphabetical order, to the bottom of the main file. + +---- + +### Security/Philosophy note + +The delegation feature is meant only for access control rules, not pubkeys. +Adding/removing pubkeys is a much more significant event than changing branch +level permissions for people already on staff, and only the main admin should +be allowed to do it. + +Gitolite's "userids" all live in the same namespace. This is unlikely to +change, so please don't ask -- it gets real complicated to do otherwise. +Allowing delegated admins to add users means username collisions, which also +means security problems (admin-A creates a pubkey for Admin-B, thus gaining +access to all of Admin-B's stuff). + +If you feel the need to delegate even that, please just go the whole hog and +give them separate gitolite instances! It's pretty easy to setup the +*software* itself system-wide, so that many users can use it without all the +"easy install" fuss. See the "system install / user setup" section in +doc/0-INSTALL.mkd for details. From 55e754a09fc7e56f9ff1c94a0dbd986e44ea872f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Apr 2010 19:56:34 +0530 Subject: [PATCH 314/850] added notes on how to do more things via admin push --- doc/2-admin.mkd | 31 ++++++++++++-------- doc/shell-games.mkd | 71 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 doc/shell-games.mkd diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index c615fb8..4190fc6 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -10,7 +10,7 @@ In this document: * moving pre-existing repos into gitolite * specifying gitweb and daemon access * custom hooks - * when you need your own "update" hook + * hook chaining * custom git config ### administer @@ -99,30 +99,37 @@ for the special usernames or remove the description line. #### custom hooks -If you want to put in your own, custom, hooks every time a new repo is created -by gitolite, put a **tested** hook script in `hooks/common` of your gitolite -clone before running easy-install. As distributed, there are only two files -there, but everything (*everything*) in that directory will get copied to the -`hooks/` subdirectory of every *new* repo created. +You can supply your own, custom, hook scripts if you wish. Just put a +**tested** hook script in `hooks/common` of your gitolite clone (as +distributed, there are only two files there). For each file in that +directory, a symlink pointing to it will be placed in the `hooks/` +subdirectory of every *new* repo created. -In order to push a new or updated hook script to *existing* repos as well, -just run easy install once again; it'll do it to existing repos also. +If you added any new hooks and wish to propagate them to *existing* repos as +well, just run gl-easy-install (or gl-setup, if you installed directly on the +server) once. **VERY IMPORTANT SECURITY NOTE: the `update` hook in `hooks/common` is what implements all the branch-level permissions in gitolite. If you fiddle with the hooks directory, please make sure you do not mess with this file accidentally, or all your fancy per-branch permissions will stop working.** -#### when you need your own "update" hook +#### hook chaining Gitolite basically takes over the update hook for all repos, but some setups really need the update hook functionality for their own purposes too. In order to allow this, Gitolite now exec's a hook called `update.secondary` when it's own "update" hook is done and everything is ready to go. -You can create this `update.secondary` hook selectively on some repos on the -server, or use the mechanism in the previous section to make gitolite install -it on *all* your repos. +You can create this `update.secondary` hook manually on selected repos on the +server, or use the mechanism in the previous section to make gitolite put it +on *all* your repos. + +Similarly, gitolite also takes over the post-update hook for the special +"gitolite-admin" repo. This hook will also chain to a `post-update.secondary` +if such a hook exists. People wishing to do exotic things on the server side +when the admin repo is pushed should see doc/shell-games.notes for how to +exploit this :-) #### custom git config diff --git a/doc/shell-games.mkd b/doc/shell-games.mkd new file mode 100644 index 0000000..8f8d621 --- /dev/null +++ b/doc/shell-games.mkd @@ -0,0 +1,71 @@ +# avoiding the shell on the server + +Gitolite now tries to prevent gitolite-admin push privileges from being used +to obtain a shell on the server. This was not always the case (older gitolite +did not make this distinction), but I've been moving towards this for a while +now, and, while there could still be holes in that separation, they will be +fixed as and when found. + +Thus, settings that have security implications can be set only from the rc +file, which needs to be edited directly on the server. And adding a new hook +requires adding it to the *gitolite* clone and running easy install again, or +gl-setup, if you used the server-side install method, both of which require +shell access. + +While this is great for my main target (corporate environments), some people +don't like it. They want to do all of this from the *gitolite-admin* repo, +because the security concern mentioned above does not bother them. They don't +want to log on to the server to make a change in the rc file or don't want to +run easy install to propagate a new set of hooks. In addition, they may want +all of these animals versioned in the "gitolite-admin" repo itself, which +certainly makes sense. + +So here's how you might do that. + +First, arrange to have all your special files added to the gitolite-admin +repo. The best option is to keep all of this in a single subdirectory (let's +call it "local" in our example). So your `~/.gitolite.rc` might go into +`local/gitolite.rc`, and all your local hooks into `local/hooks` etc. Add +them, commit, and push. + +Note: do not create any top level directory called "conf", "contrib", "doc", +"hooks", or "src" -- those names are used by gitolite itself. + +Second, create a `post-update.secondary` hook and place it in the *gitolite* +clone's `hooks/common` directory, containing the following code: + + #!/bin/bash + + GL_ADMINDIR=` cd;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'` + + cp $GL_ADMINDIR/local/gitolite.rc $HOME/.gitolite.rc + cp -a $GL_ADMINDIR/local/hooks/* $GL_ADMINDIR/hooks/common + $HOME/gitolite-install/src/gl-install -q + +Now run easy-install (or gl-setup) once, and you're done. + +All future changes to the rc file can be done via local/gitolite.rc in the +admin repo, and hooks can be added to local/hooks. + +**Warning**: Nothing in gitolite *removes* hooks, so if you delete (or even +rename) a script, it still stays on the server -- you'll have to delete them +manually from the server. + +---- + +So what's this actually doing? + +Well, first, note that `$GL_ADMINDIR` contains files from both gitolite +itself, as well as from the gitolite-admin repo. "conf/VERSION", "src", +"doc", and "hooks" come from gitolite itself, while the other 2 files in +"conf", and all of "keydir" come from the gitolite-admin repo. ("logs" +doesn't come from anywhere). + +In addition, any other files in the "master" branch of the gitolite-admin repo +get checked out here, which in this case would mean the entire "local/" +hierarchy you created above. + +Now, since the "hooks/common" directory is coming from gitolite itself, +clearly this is where the internal "install" routine expects to find new or +updated hooks to propagate. So you just copy your local hooks (in the +"local/hooks" directory) to "hooks/common" and run the installer again. Done! From 5fd9328c1cd1e7c576b6530b3253061c68b159aa Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Apr 2010 20:50:26 +0530 Subject: [PATCH 315/850] "accidental [mis]feature" -- yet another admin->shell hole blocked! This is a pretty big hole, really. Only the fact that Eli called it an "accidental feature" helped catch it :) Notes on the code: An explicit list of paths -- maybe just "conf", "keydir", and "local" -- would have been easier, but this isn't too bad, I think. --- hooks/gitolite-admin/post-update | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hooks/gitolite-admin/post-update b/hooks/gitolite-admin/post-update index 5edf96d..3707c9f 100755 --- a/hooks/gitolite-admin/post-update +++ b/hooks/gitolite-admin/post-update @@ -2,7 +2,8 @@ # checkout the master branch to $GL_ADMINDIR # (the GL_ADMINDIR env var would have been set by gl-auth-command) -GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master +GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master -- \ + `git ls-tree master | cut -f2 | perl -lne 'print unless /^(src|hooks)$/'` od=$PWD cd $GL_ADMINDIR From 813a2a990835a9422d87541cdfd1090332817722 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Apr 2010 23:46:29 +0530 Subject: [PATCH 316/850] (ls-tree has --name-only now!) thanks to Teukka for pointing it out --- hooks/gitolite-admin/post-update | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/gitolite-admin/post-update b/hooks/gitolite-admin/post-update index 3707c9f..8fcb182 100755 --- a/hooks/gitolite-admin/post-update +++ b/hooks/gitolite-admin/post-update @@ -3,7 +3,7 @@ # checkout the master branch to $GL_ADMINDIR # (the GL_ADMINDIR env var would have been set by gl-auth-command) GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master -- \ - `git ls-tree master | cut -f2 | perl -lne 'print unless /^(src|hooks)$/'` + `git ls-tree --name-only master | perl -lne 'print unless /^(src|hooks)$/'` od=$PWD cd $GL_ADMINDIR From 9b35f84f557d00190933f8204f504e158d0a2c5c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Apr 2010 10:07:59 +0530 Subject: [PATCH 317/850] fix bug in 7bfb367 that causes "@all.git" to be created! --- src/gl-compile-conf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index ab4e3d6..2d9de2f 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -389,6 +389,7 @@ wrap_chdir("$repo_base_abs"); for my $repo (sort keys %repos) { next unless $repo =~ $REPONAME_PATT; next if $repo =~ m(^EXTCMD/); # these are not real repos + next if $repo eq '@all'; unless (-d "$repo.git") { print STDERR "creating $repo...\n"; new_repo($repo, "$GL_ADMINDIR/hooks/common"); From 344fb0a2b729a87590a98e1e3cb04267de1303b7 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Apr 2010 18:26:34 +0530 Subject: [PATCH 318/850] allow user to define filenames that our hooks chain to (although the defaults are still update.secondary and post-update.secondary if you don't do anything) --- conf/example.gitolite.rc | 12 ++++++++++++ doc/2-admin.mkd | 4 ++++ hooks/common/update | 7 ++++--- hooks/gitolite-admin/post-update | 7 +++++-- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index bda9d2d..b1a921f 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -158,6 +158,18 @@ $RSYNC_BASE = ""; # Please see doc/4-wildcard-repositories.mkd for details. $GL_WILDREPOS = 0; +# -------------------------------------- +# HOOK CHAINING + +# by default, the update hook in every repo chains to "update.secondary". +# Similarly, the post-update hook in the admin repo chains to +# "post-update.secondary". If you're fine with the defaults, there's no need +# to do anything here. However, if you want to use different names or paths, +# change these variables + +# $UPDATE_CHAINS_TO = "hooks/update.secondary"; +# $ADMIN_POST_UPDATE_CHAINS_TO = "hooks/post-update.secondary"; + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 4190fc6..4b27d59 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -131,6 +131,10 @@ if such a hook exists. People wishing to do exotic things on the server side when the admin repo is pushed should see doc/shell-games.notes for how to exploit this :-) +Finally, these names (`update.secondary` and `post-update.secondary`) are +merely the defaults. You can change them to anything you want; look in +conf/example.gitolite.rc for details. + #### custom git config The custom hooks feature is a blunt instrument -- all repos get the hook you diff --git a/hooks/common/update b/hooks/common/update index 2a3e3e6..dc87d03 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -25,7 +25,7 @@ use warnings; # common definitions # ---------------------------------------------------------------------------- -our ($GL_CONF_COMPILED); +our ($GL_CONF_COMPILED, $UPDATE_CHAINS_TO); our %repos; # people with shell access should be allowed to bypass the update hook, simply @@ -123,7 +123,8 @@ my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, (shift @refs), $perm); "\t$reported_repo\t$ref\t$ENV{GL_USER}\t$log_refex\n"); # now chain to the local admin defined update hook, if present -exec "./hooks/update.secondary", @saved_ARGV - if -f "hooks/update.secondary" or -l "hooks/update.secondary"; +$UPDATE_CHAINS_TO ||= 'hooks/update.secondary'; +exec $UPDATE_CHAINS_TO, @saved_ARGV + if -f $UPDATE_CHAINS_TO or -l $UPDATE_CHAINS_TO; exit 0; diff --git a/hooks/gitolite-admin/post-update b/hooks/gitolite-admin/post-update index 8fcb182..891c273 100755 --- a/hooks/gitolite-admin/post-update +++ b/hooks/gitolite-admin/post-update @@ -11,7 +11,10 @@ $GL_BINDIR/gl-compile-conf cd $od -if [ -f hooks/post-update.secondary ] || [ -L hooks/post-update.secondary ] +ADMIN_POST_UPDATE_CHAINS_TO=` cd;perl -e 'do ".gitolite.rc"; print $ADMIN_POST_UPDATE_CHAINS_TO'` +[ -n "$ADMIN_POST_UPDATE_CHAINS_TO" ] || ADMIN_POST_UPDATE_CHAINS_TO=hooks/post-update.secondary + +if [ -f $ADMIN_POST_UPDATE_CHAINS_TO ] || [ -L $ADMIN_POST_UPDATE_CHAINS_TO ] then - exec hooks/post-update.secondary "$@" + exec $ADMIN_POST_UPDATE_CHAINS_TO "$@" fi From 850e583ac7d477f1c134bdc1085a5c7dd7d13d91 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Apr 2010 18:35:06 +0530 Subject: [PATCH 319/850] changelog for v1.4 --- doc/CHANGELOG | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 4dd89b7..d1c1584 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -1,5 +1,20 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately +[NYD = not yet documented due to lack of time...] + + - v1.4 + + - recurse through keydir for pubkeys + - bypass update hook if GL_BYPASS_UPDATE_HOOK is available in ENV + - new server-side program "gl-tool", subcommand "shell-add" + - new "D" permission (makes RW+ no longer imply "D" if used) + - @all for repos is now a true @all + - allow setperms to specify @all + - post-update hook and gl-setup should be dash compat now + - workaround for a Data::Dumper crash; see 412a691 + - both hooks chain to ".secondary" now + + - new style personal branches (see 2456cc1 for advantages) - v1.3 @@ -13,12 +28,12 @@ the tags can help you position stuff approximately - v1.1 - contrib directory added - - expand now lists non-wildcard repos also - - refs also have groups now - - allow admins to get "info" for other users + - expand now lists non-wildcard repos also (expand is NYD) + - refs also have groups now (NYD) + - allow admins to get "info" for other users (NYD) - wildrepos merged - - getdesc and setdesc for wildrepos + - getdesc and setdesc for wildrepos (NYD) - htpasswd subcommand - access control for rsync From 9df775413cbdae53a119767f7399f3a4c7a16af8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 13 Apr 2010 23:06:40 +0530 Subject: [PATCH 320/850] document the change in a982446 (thanks to Eli for catching this!) --- doc/4-wildcard-repositories.mkd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 3717aca..9b01dcb 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -108,11 +108,11 @@ metacharacters. Just for interest, note that this is in contrast to the refexes for the normal "branch" permissions, as described in `conf/example.conf` and elsewhere. -Those "refexes" are *not* anchored; a pattern like `refs/heads/master` -actually matches `foo/refs/heads/master01/bar` as well, even if no one will -actually push such a branch! You can anchor it if you really care, by using -`master$` instead of `master`, but anchoring is *not* the default for -refexes.] +These "refexes" are only anchored at the start; a pattern like +`refs/heads/master` actually can match `refs/heads/master01/bar` as well, even +if no one will actually push such a branch! You can anchor both sides if you +really care, by using `master$` instead of `master`, but that is *not* the +default for refexes. ### Handing out rights to wildcard-matched repos From 0f6079c7a6d3b02aeb163cfeace32955634e9b53 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 14 Apr 2010 06:53:01 +0530 Subject: [PATCH 321/850] added gerrit comparision --- contrib/gerrit.mkd | 111 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 contrib/gerrit.mkd diff --git a/contrib/gerrit.mkd b/contrib/gerrit.mkd new file mode 100644 index 0000000..f08a1af --- /dev/null +++ b/contrib/gerrit.mkd @@ -0,0 +1,111 @@ +# comparing gerrit and gitolite + +Gerrit and gitolite have too many high level differences. Size is most +visible of course: 56000 lines of Java versus 1300 lines of perl+shell, +according to David A. Wheeler's 'SLOCCount' tool. Gerrit needs a database, +and even comes with its own ssh server and git server, and since the git +engine is internal it probably has to include a lot of things that normal git +already has; I wouldn't know for sure. + +Gerrit allows a lot more de-centralisation in managing the system, and of +course excels at code review, and it seems geared to really large, open +source-ish projects with lots of contributors. + +Gitolite works on a pure command-line install and a plain text file config, +and is designed to run unobtrusively and quite transparently to all developers +-- other than sending the admin their pubkey, nothing really changes for them +in their workflow, toolset, etc. The "lite" in the name still holds, despite +all the extra features being pumped in! + +Gitolite was mainly written for a corporate environment, where we really, +really, need branch-level ACLs. While they would certainly love the code +review part, things like *voting* on a change, and so on seem a bit alien, and +it seems to me that code review itself is more likely to be a hierarchical +thing, not a peer-to-peer, "anyone can comment" thing. I could be wrong. + +---- + +In short, gitolite doesn't do the main thing that gerrit does, and gerrit is +so much bigger than gitolite in so many ways, it seems really odd to compare +them at all. + +However, it seems gerrit comes closest to gitolite in terms of flexibility of +access control, which is gitolite's main strength, so I thought it would be +useful to compare gitolite with just what is in the "access-control.html" in +the gerrit war file. Or see [this][gdac]. [...and stop sniggering at the +"svn" in the link dammit!] + +[gdac]: http://gerrit.googlecode.com/svn/documentation/2.1.2/access-control.html +[jwzq]: http://regex.info/blog/2006-09-15/247 + +**Administrators**: anyone who has gitolite-admin push privs + +**Anonymous Users**: gitolite doesn't do that, though the "ssh-plus" branch, +combined with git-daemon2 (Ilari) will allow that in future. When git-daemon2 +becomes mainstream, the supporting code in this branch will also be merged +into "master". + +**Registered Users**: @all + +**Account Groups**: @groups in gitolite. We do allow them to be nested, +although the parsing is single-pass. We also don't have group `foo-admin` +managing membership to group `foo` though; all groups are managed by the eqvt +of "Administrators". + +**Project ACLs**: first, let's remember (again) that we don't have any of the code +review stuff :) + + * **Reference-level access controls** make no mention of regexes. I'm not + [JWZ][jwzq], and I strongly consider regexes a plus point :) + + * They also make no mention of giving permissions to individual users, only + groups. If this is true, it would be a little cumbersome when managing + many small projects -- projects where you have only one QA, one + integrator, etc., would end up making many 1-man groups. [If anyone who's + used gerrit can correct me on this I'd appreciate it]. + + * **Evaluation** order and priority of access control rules are different. + Gerrit goes by specificity, gitolite goes by sequence. It shouldn't + matter; they're probably equivalent except perhaps in some far-fetched + scenarios. + + * One big difference is that gitolite does not process "deny" rules ("-1 no + Access" in gerrit terms) for *read* access -- we only support those for + write access. Gerrit uses this to "hide a handful of projects on an + otherwise public server"; in gitolite you'd better avoid giving `R = @all` + in the first place :) + + * [Update 2010-04-14: it appears that Gerrit is also in the process of + implementing *read* access control at the branch level -- they can afford + to even think of that because they have a full jgit stack to play with. + Gitolite is dependent on git itself to provide that -- it just cannot be + done without support from git core. I can see some corporates drooling at + this possibility (makes no sense for open source projects IMO) ;-)] + +**Categories**: + + * gitolite doesnt have an "owner" for each project in any administrative + sense. Perhaps you could consider whoever has `RW+` perms to be an owner + but it doesn't go beyond what that implies. + + * gitolite doesnt do anything special to signed or annotated tags + + * gitolite always allows creating a branch. The only way to prevent that is + to list out allowed branches explicitly (make sure you end the refex with + a `$`!). + + * Force push is the same as delete: historically (and by default, even now) + gitolite does the same . However, I've only recently (and somewhat + reluctantly) changed gitolite to allow treating these two separately. + + Of course, direct pushing clashes with code review, and gerrit recommends + that if you want code review you should not use this feature. [Normal + pushes in gerrit go through a temp branch that is moved to the correct one + after a review is done; direct pushes are all that gitolite has]. + + * author/committer identity: checking these fields in pushed commits is + likely to be important in some projects, but gitolite doesn't have any + notion of this. Hmm... I smell another feature in the future :) + +The rest of it is in areas that the two tools have no overlap on (again, code +review being the main thing). From 8d55bd722c3cc6ddd40e4f99ffc93e6416b8f736 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 14 Apr 2010 09:49:09 +0530 Subject: [PATCH 322/850] (minor fixup) --- src/gl-compile-conf | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 2d9de2f..60046df 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -75,8 +75,7 @@ $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; # command and options for authorized_keys $AUTH_COMMAND="$bindir/gl-auth-command"; -$AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding"; - # note, for most users there's also a "no-pty" added to this, see later +$AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; # groups can now represent user groups or repo groups. @@ -544,7 +543,7 @@ for my $pubkey (`find . -type f`) print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n"; next; } - print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS,no-pty "; + print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; print $newkeys_fh $pubkey_content; } # lint check 3; a little more severe than the first two I guess... From 461a58132292e53089bd4b3a75e7bddcc1edcd75 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 14 Apr 2010 23:16:29 +0530 Subject: [PATCH 323/850] (minor) document what to do when you have *two* gits ...and the wrong one ends up runing --- doc/3-faq-tips-etc.mkd | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 5939832..f0c7afa 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -485,6 +485,17 @@ attempting to run git stuff. Very easy, very simple, and completely transparent to the users :-) +**Note**: sometimes you have a system that already has an older "git" +installed in one of the system PATHs, but you've installed a newer git in some +non-standard location and want that picked up. Because of security reasons, +gitolite will not prepend `GIT_PATH` to the PATH variable, so the older git +comes first and it gets kinda frustrating! + +Here's a simple workaround. Ignore the `GIT_PATH` variable, and directly set +the full PATH in the rc file, like so: + + $ENV{PATH} = "/home/sitaram/bin:$ENV{PATH}"; + #### "personal" branches "personal" branches are great for corporate environments, where From 2a776e56ad59aa4d839d3e4ea70e0252f13c9462 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Apr 2010 06:32:39 +0530 Subject: [PATCH 324/850] "D" must be combined with RW or RW+ (warning: minor backward compat breakage) Having to specify "D" separately from RW or RW+ was cumbersome, and although I don't actually use this feature, I can see the point. One way to think of this is: - RW and RW+ were the only existing branch level rights - it doesnt make sense to have D rights without W (hence RW) rights - so we simply suffix a D to these if required. Thus you can have RW, RW+, RWD, RW+D. I hope the (hopefully few) of you who have started to use this feature will convert your configs when you next upgrade to "pu". I now regret pushing the previous syntax to master too quickly -- lots of people use master only, and on the next promotion of pu the syntax will change. To reduce this exposure, this change will be promoted to master very soon. --- conf/example.conf | 8 ++++---- doc/3-faq-tips-etc.mkd | 20 +++++++++++--------- src/gl-compile-conf | 4 ++-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 6d8c3d7..5a5b36a 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -67,13 +67,13 @@ # start line: # repo [one or more repos and/or repo groups] # followed by one or more permissions lines: -# (C|D|R|RW|RW+) [zero or more refexes] = [one or more users] +# (C|R|RW|RW+|RWD|RW+D) [zero or more refexes] = [one or more users] -# there are 5 types of permissions: R, RW, and RW+ are simple (the "+" means +# there are 6 types of permissions: R, RW, and RW+ are simple (the "+" means # permission to "rewind" -- force push a non-fast forward to -- a branch). # The C permission is described in doc/4-wildcard-repositories.mkd. The D -# permission is described in doc/3-faq-tips-etc.mkd, in the "advanced -# features" section. +# addition to RW/RW+ is described in doc/3-faq-tips-etc.mkd, in the section on +# "separating delete and rewind rights". # how permissions are matched: # - user, repo, and access (W or +) are known. For that combination, if diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index f0c7afa..4c2d43b 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -18,7 +18,7 @@ In this document: * two levels of access rights checking * better logging * "exclude" (or "deny") rules - * the "D" permission -- separating delete and rewind rights + * separating delete and rewind rights * file/dir NAME based restrictions * delegating parts of the config file * convenience features @@ -367,7 +367,7 @@ And here's how it works: before the third one, and it has a `-` as the permission, so the push fails -#### the "D" permission -- separating delete and rewind rights +#### separating delete and rewind rights Since the beginning, `RW+` meant being able to rewind *or* delete a ref. My stand is that these two are fairly similar, and infact a rewind is almost the @@ -379,13 +379,15 @@ situations where one of them should be restricted more than the other. ([Arguments][sdrr] exist for both sides: restrict delete more than rewind, and vice versa). -So we now allow these two rights to be separated. Just use the new `D` -permission anywhere in the config for the repo, and instantly all `RW+` -permissions (for that repo) cease to permit deletion of the ref matched. +So we now allow these two rights to be separated. Here's how: -This provides the *greatest* backward compatibility (if you don't specify any -`D` permissions, everything works just as before), while also enabling the new -semantics at the granularity of a repo, instead of the entire config. + * branch deletion is permitted by using `RWD` or `RW+D` -- essentially the + current branch permissions with a `D` suffixed + * if a repo has a rule containing such a `D`, all `RW+` permissions (for + that repo) cease to permit deletion of the ref matched. + +This provides the *greatest* backward compatibility, while also enabling the +new semantics at the granularity of a repo, instead of the entire config. Note 1: if you find that `RW+` no longer allows deletion but you can't see a `D` permission in the rules, remember that gitolite allows a repo config to be @@ -395,7 +397,7 @@ files. Be sure to search everywhere :) Note 2: a quick way to make this the default for *all* your repos is: repo @all - D dummy-branch = foo + RWD dummy-branch = foo where foo can be either the administrator, or if you can ignore the warning message when you push, a non-existant user. diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 60046df..50c2668 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -190,7 +190,7 @@ sub parse_conf_file s/\bCREAT[EO]R\b/\$creater/g for @repos; } # actual permission line - elsif (/^(-|C|D|R|RW|RW\+) (.* )?= (.+)/) + elsif (/^(-|C|R|RW\+?D?) (.* )?= (.+)/) { my $perms = $1; my @refs; @refs = split(' ', $2) if $2; @@ -261,7 +261,7 @@ sub parse_conf_file # if the user specified even a single 'D' anywhere, make # that fact easy to find; this changes the meaning of RW+ # to no longer permit deletes (see update hook) - $repos{$repo}{DELETE_IS_D} = 1 if $perms eq 'D'; + $repos{$repo}{DELETE_IS_D} = 1 if $perms =~ /D/; # for 2nd level check, store each "ref, perms" pair in order for my $ref (@refs) From 8a4cccf2369a461fa963fded8c7f79c6f740e649 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 16 Apr 2010 06:31:04 +0530 Subject: [PATCH 325/850] doc/7 and doc/0: how to clear out a botched install --- doc/0-INSTALL.mkd | 44 ++++++++++++++++++++++++++---------- doc/7-install-transcript.mkd | 12 ++++++++++ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 4351a46..59f32dc 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -259,17 +259,21 @@ Sometimes you might find gitolite is overkill -- you have only one user (yourself) pushing maybe. Or maybe gitolite is just not enough -- you want a web-based front end that users can use to manage their keys themselves, etc., in which case you'd probably switch to [github][g1], [girocco][g2], -[indefero][g3] or [gitorious][g4]. Either way, you'd like to uninstall -gitolite. +[indefero][g3] or [gitorious][g4]. [Gerrit][g5] is quite nice too, if you +want collaborative code review there's nothing like it. Either way, you'd +like to uninstall gitolite. [g1]: http://github.com [g2]: http://repo.or.cz/w/girocco.git [g3]: http://www.indefero.net/ [g4]: http://gitorious.com/ +[g5]: http://code.google.com/p/gerrit/ -Uninstalling gitolite is fairly easy. Just log on to the server and do the -following (assuming `$REPO_BASE` in the rc file was left at its default of -`~/repositories`; if not, adjust accordingly): +Uninstalling gitolite is fairly easy, although it is manual. (We'll assume +`$REPO_BASE` in the rc file was left at its default of `~/repositories`; if +not, adjust accordingly): + +**server side tasks** * edit `~/.ssh/authorized_keys` and delete the `# gitolite start` and `# gitolite end` markers and all the lines between them. This will prevent @@ -287,18 +291,34 @@ following (assuming `$REPO_BASE` in the rc file was left at its default of ~/.gitolite.rc ~/repositories/gitolite-admin.git - * Then remove all the `update` hooks that git installs on each repository. - The easiest way is: + * You can remove all of `~/repositories` if you have not really started + using gitolite properly yet; that's your choice. + + If you *do* need to preserve the other repos and continue to use them, + remove all the `update` hooks that git installs on each repository. The + easiest way is: find ~/repositories -wholename "*.git/hooks/update" | xargs rm -f but you can do it manually if you want to be careful. - * Finally, any remote users that still have access must update their clone's - remote URLs (edit `.git/config` in the repo) to prefix `repositories/` - before the actual path used, in order for the remote to still work. This - is because you'll now be accessing it through plain ssh, which means you - have to give it the full path. +**client side tasks** + + * Any remote users that still have access must update their clone's remote + URLs (edit `.git/config` in the repo) to prefix `repositories/` before the + actual path used, in order for the remote to still work. This is because + you'll now be accessing it through plain ssh, which means you have to give + it the full path. + + * Finally, you as the gitolite admin will probably have a host stanza for + "gitolite" in your *client*'s `~/.ssh/config`. Find and delete lines that + look like this: + + host gitolite + user git + hostname your.server + port 22 + identityfile ~/.ssh/your-gitolite-admin-username ### appendix C: NOTE TO PACKAGE MAINTAINERS diff --git a/doc/7-install-transcript.mkd b/doc/7-install-transcript.mkd index 026c98e..0127d46 100644 --- a/doc/7-install-transcript.mkd +++ b/doc/7-install-transcript.mkd @@ -17,6 +17,18 @@ transcript is **non**-gitolite stuff :) ---- +### (cleaning out a botched install if needed) + +When people have trouble installing gitolite, they often try to change a bunch +of things manually on the server. This usually makes things worse ;-) so if +you're reading this document as a follow-up to a failed install, please +**first** clean out the botched install (instructions [here][appB]) and +**then** continue with this document. + +[appB]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#uninstall + +---- + ### create userids on server and client (optional) Client side: add user, give him a password From 8b31956c32cd4c49b28478fd4c897c6ee5dc3977 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Fri, 16 Apr 2010 09:19:07 +0200 Subject: [PATCH 326/850] Do not override the SSH port if standard port 22 is used Always passing "-p 22" to ssh (or "-P 22" to scp) if no custom port is given on the command line causes trouble when not using a host name but an SSH session name (as defined in .ssh/config) which defines a non-standard port, because the port given on the command line overrides that port. Signed-off-by: Sebastian Schuberth --- src/gl-easy-install | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 7a38f13..c7f522f 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -48,7 +48,7 @@ main() { [[ $upgrade == 0 ]] && initial_conf_key # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf" - ssh -p $port $user@$host "cd $GL_ADMINDIR; \$PWD/src/gl-compile-conf $quiet" + ssh $p_port $user@$host "cd $GL_ADMINDIR; \$PWD/src/gl-compile-conf $quiet" setup_pta @@ -183,7 +183,8 @@ basic_sanity() { # That is, running "ssh git@server" should log in straight away, without # asking for a password - ssh -p $port -o PasswordAuthentication=no $user@$host true || + [[ $port -ne 22 ]] && p_port="-p $port" + ssh $p_port -o PasswordAuthentication=no $user@$host true || die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something" } @@ -200,7 +201,7 @@ version_info() { # what was the old version there? export upgrade_details="you are upgrading \ - $(ssh -p $port $user@$host cat gitolite-install/conf/VERSION 2>/dev/null || echo '(or installing first-time)' ) \ + $(ssh $p_port $user@$host cat gitolite-install/conf/VERSION 2>/dev/null || echo '(or installing first-time)' ) \ to $(cat conf/VERSION)" prompt "$upgrade_details" "$v_upgrade_details" @@ -281,8 +282,8 @@ copy_gl() { # server, to a directory called (for example) "gitolite-install". You may # have to create the directory first. - ssh -p $port $user@$host mkdir -p gitolite-install - scp $quiet -P $port -p -r src conf doc hooks $user@$host:gitolite-install/ + ssh $p_port $user@$host mkdir -p gitolite-install + scp $quiet ${p_port/p/P} -p -r src conf doc hooks $user@$host:gitolite-install/ # MANUAL: now log on to the server (ssh git@server) and get a command # line. This step is for your convenience; the script does it all from @@ -296,7 +297,7 @@ copy_gl() { prompt "finding/creating gitolite rc..." "$v_edit_glrc" # lets try and get the file from there first - if scp -P $port $user@$host:.gitolite.rc $tmpgli &>/dev/null + if scp ${p_port/p/P} $user@$host:.gitolite.rc $tmpgli &>/dev/null then prompt " ...trying to reuse existing rc" \ "Oh hey... you already had a '.gitolite.rc' file on the server. @@ -329,7 +330,7 @@ Let's see if we can use that instead of the default one..." fi # copy the rc across - scp $quiet -P $port $tmpgli/.gitolite.rc $user@$host: + scp $quiet ${p_port/p/P} $tmpgli/.gitolite.rc $user@$host: } run_install() { @@ -337,18 +338,18 @@ run_install() { prompt "installing/upgrading..." "$v_ignore_stuff" # extract the GL_ADMINDIR, REPO_BASE and GIT_PATH locations - GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") - REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") - GIT_PATH=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GIT_PATH'") + GL_ADMINDIR=$(ssh $p_port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'") + REPO_BASE=$( ssh $p_port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'") + GIT_PATH=$( ssh $p_port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GIT_PATH'") # determine if this is an upgrade; we decide based on whether a file # called $GL_ADMINDIR/conf/gitolite.conf exists on the remote side. We # can't do this till we know the correct value for GL_ADMINDIR upgrade=0 - if ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf &> /dev/null + if ssh $p_port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf &> /dev/null then upgrade=1 - ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf 2> /dev/null | grep '@SHELL' && + ssh $p_port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf 2> /dev/null | grep '@SHELL' && prompt "" "$v_at_shell_bwi" [[ -n $admin_name ]] && echo -e "\n *** WARNING ***: looks like an upgrade... ignoring argument '$admin_name'" else @@ -358,7 +359,7 @@ run_install() { # MANUAL: still in the "gitolite-install" directory? Good. Run # "src/gl-install" - ssh -p $port $user@$host "cd gitolite-install; src/gl-install $quiet" + ssh $p_port $user@$host "cd gitolite-install; src/gl-install $quiet" # MANUAL: if you're upgrading, run "src/gl-compile-conf" and you're done! # -- ignore the rest of this file for the purposes of an upgrade @@ -388,8 +389,8 @@ repo testing " > $tmpgli/gitolite.conf # send the config and the key to the remote - scp $quiet -P $port $tmpgli/gitolite.conf $user@$host:$GL_ADMINDIR/conf/ - scp $quiet -P $port "$HOME/.ssh/$admin_name.pub" $user@$host:$GL_ADMINDIR/keydir + scp $quiet ${p_port/p/P} $tmpgli/gitolite.conf $user@$host:$GL_ADMINDIR/conf/ + scp $quiet ${p_port/p/P} "$HOME/.ssh/$admin_name.pub" $user@$host:$GL_ADMINDIR/keydir } # ---------------------------------------------------------------------- @@ -413,13 +414,13 @@ setup_pta() { PATH=\$PATH:$GIT_PATH GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet 2>/dev/null || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start -" | ssh -p $port $user@$host +" | ssh $p_port $user@$host # MANUAL: now that the admin repo is created, you have to set the hooks # properly. The install program does this. So cd back to the # "gitolite-install" directory and run "src/gl-install" - ssh -p $port $user@$host "cd gitolite-install; src/gl-install $quiet" + ssh $p_port $user@$host "cd gitolite-install; src/gl-install $quiet" # MANUAL: you're done! Log out of the server, come back to your # workstation, and clone the admin repo using "git clone From 34aad344783d95a4329fd5db1b983a1ffe150fe3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 16 Apr 2010 19:19:50 +0530 Subject: [PATCH 327/850] make "expand" also print version, like "info" does (thanks to Ilari for catching this) --- src/gitolite.pm | 15 +++++++++++---- src/gl-auth-command | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 9850af8..3bc513f 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -277,6 +277,12 @@ sub parse_acl # print a report of $user's basic permissions # ---------------------------------------------------------------------------- +sub report_version { + my($GL_ADMINDIR, $user) = @_; + print "hello $user, the gitolite version here is "; + system("cat", ($GL_PACKAGE_CONF || "$GL_ADMINDIR/conf") . "/VERSION"); +} + # basic means wildcards will be shown as wildcards; this is pretty much what # got parsed by the compile script sub report_basic @@ -286,9 +292,8 @@ sub report_basic &parse_acl($GL_CONF_COMPILED, "", "CREATER", "READERS", "WRITERS"); # send back some useful info if no command was given - print "hello $user, the gitolite version here is "; - system("cat", ($GL_PACKAGE_CONF || "$GL_ADMINDIR/conf") . "/VERSION"); - print "\ryou have the following permissions:\r\n"; + &report_version($GL_ADMINDIR, $user); + print "\rthe gitolite config gives you the following access:\r\n"; for my $r (sort keys %repos) { # @all repos; meaning of read/write flags: # @ => @all users are allowed access to this repo @@ -307,8 +312,10 @@ sub report_basic sub expand_wild { - my($GL_CONF_COMPILED, $repo_base_abs, $repo, $user) = @_; + my($GL_ADMINDIR, $GL_CONF_COMPILED, $repo_base_abs, $repo, $user) = @_; + &report_version($GL_ADMINDIR, $user); + print "\ryou have access to the following repos on the server:\r\n"; # this is for convenience; he can copy-paste the output of the basic # access report instead of having to manually change CREATER to his name $repo =~ s/\bCREAT[EO]R\b/$user/g; diff --git a/src/gl-auth-command b/src/gl-auth-command index 583cd4e..24fc5b9 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -127,7 +127,7 @@ if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) { elsif ($verb eq 'expand') { # with a wildcard, you can "expand" it to see what repos actually match die "$repo has invalid characters" unless "x$repo" =~ $REPOPATT_PATT; - expand_wild($GL_CONF_COMPILED, $repo_base_abs, $repo, $user); + expand_wild($GL_ADMINDIR, $GL_CONF_COMPILED, $repo_base_abs, $repo, $user); } else { die "$cmd doesn't make sense to me\n"; } From fabeedf103e44274ab7f943f61aec433217e0b8f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 16 Apr 2010 19:49:22 +0530 Subject: [PATCH 328/850] document the info and expand commands --- doc/3-faq-tips-etc.mkd | 42 +++++++++++++++-------------- doc/report-output.mkd | 61 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 doc/report-output.mkd diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 4c2d43b..3e5b77c 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -430,28 +430,30 @@ Sometimes there are too many repos, maybe even named similarly, or with the potential for typos, confusion about hyphens/underscores or upper/lower case, etc. You'd just like a simple way to know what repos you have access to. -Easy! Just use ssh and try to log in as if you were attempting to get a -shell: +Easy! Just use ssh to give the "info" command to the gitolite server: - $ ssh gitolite info - PTY allocation request failed on channel 0 - hello sitaram, the gitolite version here is v0.6-17-g94ed189 - you have the following permissions: - R W Anu-WSD - R ROtest - R W SecureBrowse - R W entrans - R W git-notes - R W gitolite - R W gitolite-admin - R W indic_web_input - R W proxy - @ @ testing - R W vkc + $ ssh git@server info + hello sitaram, the gitolite version here is v1.4-9-g34aad34 + the gitolite config gives you the following access: + R W SecureBrowse + R W anu-wsd + R W entrans + @ W git-notes + @ W gitolite + R W gitolite-admin + R W indic_web_input + @ R private/CREATER/[\w.-]+ + R W proxy + @ @ W public/CREATER/[\w.-]+ + @ @ testing + R W vkc -Note that until this version, we used to put out an ugly `need -SSH_ORIGINAL_COMMAND` error, just like gitosis used to. All we did is put -that code path to better use :-) +To understand what these symbols mean, please see doc/report-output.mkd. The +administrator can also say things like: + + ssh gitolite info u1 u2 u3 + +to get this info for other user(s). #### error checking the config file diff --git a/doc/report-output.mkd b/doc/report-output.mkd new file mode 100644 index 0000000..a0dd87d --- /dev/null +++ b/doc/report-output.mkd @@ -0,0 +1,61 @@ +# output of the "info" and "expand" commands + +Running "ssh git@server info" or "ssh git@server expand" gives you certain +output. This doclet describes the output; you're welcome to help me make it +clearer :) + +There are 3 columns of permissions (create, read, and write) in the output, +although the first column is often blank. + +Here is the output of the 2 commands (info and expand): + +### the "info" command + +Usage: + + ssh git@server info + ssh git@server info [list of users] + +The "info" command shows you all the repos (and repo patterns) in the config +file that you have been given any kind of access to. If you're an admin you +can append a list of users to see their permissions instead of your own. +(Side note: if you installed using easy-install that would probably be `ssh +gitolite info`, by the way). + + * col 1 (exists only if the "repo" name is actually a pattern) + * `@`: `@all` users have permission to create repos matching this + pattern + * `C`: this user has permission to create repos matching this pattern + + * col 2 + * `@`: `@all` users have read access to this repo + * `r`: this user has read access to `@all` repos + * `R`: this user has read access to this repo + + * col 3: same as col 2, but for write access instead of read + +### the "expand" command + +Usage: + + ssh git@server expand [optional pattern] + +The "expand" command trawls through all the repositories on the server, +limiting to repos matching the pattern you provide (default is all repos +found). + +For each repo found, it searches for it in the config -- either the actual +repo entry (when the repo is not a wildcard repo), or an entry for the +wildcard that matches it -- and reports permissions. It also takes into +account extra permissions enabled by the `setperms` command (see +doc/4-wildcard-repositories.mkd). It shows you the "creater" of the repo as +an additional column, defaulting to `` if it was not a wildcard +repo. + + * col 1: not used + + * col 2: same as col 2 for info, except substitute "repo" with "repo or + wildcard matching this repo" + + * col 3: same as col 2, but for write access instead of read + From 45ecc518f05b78c8dabe0b3d22fcd43588991f59 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 19 Apr 2010 21:42:42 +0530 Subject: [PATCH 329/850] WARNING: WE NOW REFUSE TO RUN IF GIT ON THE SERVER IS < 1.6.2 I just got tired of supporting old gits. Sorry. Had to happen sooner or later. I know you feel upset right now but later you'll thank me. --- doc/3-faq-tips-etc.mkd | 27 +-------------------------- doc/CHANGELOG | 5 +++++ src/gl-compile-conf | 6 +++--- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 3e5b77c..ff8dc09 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -61,32 +61,7 @@ In this document: ## git version dependency -Here's a workaround for a version dependency that the normal flow of gitolite -has. - -When you edit your config file to create a new repo, and push the changes to -the server, gitolite creates an empty, bare repo for you. Normally, you're -expected to clone this on the client side, and start working -- make your -first commit(s), then push, etc. - -However, cloning an empty repo requires a server side git version that is at -least 1.6.2. Gitolite detects this when creating a repo, and warns you. - -The workaround is to use the older (gitosis-style) method on the client: -create an empty repo locally, make a commit or two, set an "origin" remote, -and then push. Something like: - - mkdir my-new-project - cd my-new-project - git init - git commit --allow-empty -m 'Initial repository' - # or, if your client side git is too old for --allow-empty, just make some - # files, "git add" them, then "git commit" - git remote add origin git@gitolite-server:my-new-project.git - git push origin master:master - -Once this is done, the repo is available for cloning by anyone else in the -normal way, since it's not empty anymore. +Gitolite (on the server) now refuses to run if git is not at least 1.6.2. ## other errors, warnings, notes... diff --git a/doc/CHANGELOG b/doc/CHANGELOG index d1c1584..32de001 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,6 +2,11 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] + - REFUSE TO RUN ON SERVER GIT < 1.6.2 (do NOT upgrade gitolite to or beyond + this point if you are unable to upgrade git itself to at least 1.6.2) + + - "D" must be combined with RW or RW+ (warning: minor backward compat breakage) + - v1.4 - recurse through keydir for pubkeys diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 50c2668..9411c4d 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -408,10 +408,10 @@ for my $repo (sort keys %repos) { } } -warn "\n\t\t***** WARNING *****\n" . +die "\n\t\t***** AAARGH! *****\n" . "\tyour git version is older than 1.6.2\n" . - "\tgitolite will work but you MUST read the section on\n" . - "\t\"git version dependency\" in doc/3-faq-tips-etc.mkd\n" + "\tsince that is now more than one year old, and gitolite needs some of\n" . + "\tthe newer features, please upgrade.\n" if $git_version < 10602; # that's 1.6.2 to you # ---------------------------------------------------------------------------- From 1e06fea3b6959faeb72d8dca46cd4753ada48637 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 20 Apr 2010 19:01:05 +0530 Subject: [PATCH 330/850] (security) a different fix in place of 5fd9328 SECURITY NOTE: if you deleted or renamed a pubkey file after 5fd9328 went in (April 12th), please: - upgrade asap, then - go to your latest gitolite-admin clone and "git push -f" Otherwise this is not urgent. 5fd9328 (and its minor successor 813a2a9) were about preventing the gitolite admin from sneaking in files to src/ and hooks/ into $GL_ADMINDIR. It seemed easy enough to do this by converting the path-less checkout to a with-paths checkout, but this has caused a worse problem -- deleting a keydir/foo.pub now no longer has an effect; the file still hangs around in the work tree. Ouch! (and thanks to teukka for noticing) We now do this check as a separate step, so the checkout can revert to being path-less. --- hooks/gitolite-admin/post-update | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hooks/gitolite-admin/post-update b/hooks/gitolite-admin/post-update index 891c273..0d1ba99 100755 --- a/hooks/gitolite-admin/post-update +++ b/hooks/gitolite-admin/post-update @@ -1,9 +1,17 @@ #!/bin/sh +# ensure that the admin is not sneaking in src/ and hooks/ :) +GIT_WORK_TREE=$GL_ADMINDIR git ls-tree --name-only master | + perl -lne 'exit 1 if /^(src|hooks)$/' || { + echo "*** ERROR ***" >&2 + echo "no files/dirs called 'src' or 'hooks' are allowed, sorry" >&2 + echo "until those files are deleted, the post-update hook will not run" >&2 + exit 1 + } + # checkout the master branch to $GL_ADMINDIR # (the GL_ADMINDIR env var would have been set by gl-auth-command) -GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master -- \ - `git ls-tree --name-only master | perl -lne 'print unless /^(src|hooks)$/'` +GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master od=$PWD cd $GL_ADMINDIR From 38337551b12913f2f22d60a43b0768416ba453c4 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 20 Apr 2010 21:16:24 +0530 Subject: [PATCH 331/850] (minor) helpful message when git isn't found in PATH on server --- src/gl-compile-conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 9411c4d..5a81147 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -375,6 +375,12 @@ close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; # but it turns out not everyone has "modern" gits :) my $git_version = `git --version`; +die " + *** ERROR *** + did not get a proper version number. Please see if git is in the PATH on + the server. If it is not, please edit ~/.gitolite.rc on the server and + set the \$GIT_PATH variable to the correct value\n +" unless $git_version; my ($gv_maj, $gv_min, $gv_patchrel) = ($git_version =~ m/git version (\d+)\.(\d+)\.(\d+)/); die "$ABRT I can't understand $git_version\n" unless ($gv_maj >= 1); $git_version = $gv_maj*10000 + $gv_min*100 + $gv_patchrel; # now it's "normalised" From 820d3f594819ce6688684f8909bbe4b30a8e8dfd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 22 Apr 2010 01:12:59 +0530 Subject: [PATCH 332/850] the most common problems an admin will see an admin who refuses to read messages that show up on the screen, that is ;-) --- doc/6-ssh-troubleshooting.mkd | 60 ++++++++++++++++++++++++++++------- src/gl-easy-install | 2 +- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 826a5ff..83da139 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -2,6 +2,7 @@ In this document: + * the most common problems that an admin will see * basic ssh troubleshooting * passphrases versus passwords * ssh-agent problems @@ -29,19 +30,54 @@ In addition to both these documents, there's now a program called `sshkeys-lint` that you can run on your client. Run it without arguments to get help on how to run it and what inputs it needs. -Please also note that ssh problems don't always look like ssh problems. One -common example: when the remote says the repo you're trying to access "does -not appear to be a git repository", and yet you are sure it exists, you -haven't mis-spelled it, etc. Another example is being able to access -repositories using the full unix path (typically like -`git@server:repositories/reponame.git`, assuming default `$REPO_BASE` setting, -instead of specifying only the part below `$REPO_BASE`, i.e., -`git@server:reponame.git`). +### the most common problems that an admin will see -[Both these errors indicate that you managed to bypass gitolite completely and -are using your shell access -- instead of running via -`/some/path/gl-auth-command ` it is just going to bash and -working from there!] +Ironically, these problems **only** happen to the person who installed +gitolite using easy-install, and has **utterly failed** to read/heed the +message that shows up at the end of running that command. This is because +only the admin has *two* ssh keys to the server (see "basic ssh +troubleshooting for the main admin" section below for more on this). + +Both these problems are caused by using the wrong key, thus **bypassing gitolite +completely**: + + * you get `fatal: 'reponame' does not appear to be a git repository`, and + yet you are sure 'reponame' exists, you haven't mis-spelled it, etc. + + * you are able to clone repositories but are unable to push changes back. + +Let us recap the message that appears on a successful run of the "easy-install" +program; it looks something like this (with suitable values substituted for +``, ``, and ``): + + IMPORTANT NOTE -- PLEASE READ!!! + *Your* URL for cloning any repo on this server will be + gitolite:reponame.git + + *Other* users you set up will have to use + @:reponame.git + However, if your server uses a non-standard ssh port, they should use + ssh://@:/reponame.git + + If this is your first time installing gitolite, please also: + tail -31 src/gl-easy-install + for next steps. + +The first error above happens if you use `git@server:reponame` instead of +`gitolite:reponame`. All your repos are actually in a subdirectory pointed to +by `$REPO_BASE` in the rc file (default: `repositories`). Gitolite internally +prefixes this before calling the actual git command you invoked, but since +you're bypassing gitolite completely, this prefixing does not happen, and so +the repo is not found. + +The second error happens if you use `git@server:repositories/reponame.git` +(assuming default `$REPO_BASE` setting) -- that is, you used the full unix +path. Since the "prefixing" mentioned above is not required, the shell finds +the repo and clones ok. But when you push, gitolite's **update hook** kicks +in, and fails to run because you some of the environment variables it is +expecting are not present. + +---- diff --git a/src/gl-easy-install b/src/gl-easy-install index c7f522f..fdb8776 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -567,7 +567,7 @@ through some other means than the $admin_name key. v_done=" done! -Reminder: +IMPORTANT NOTE -- PLEASE READ!!! *Your* URL for cloning any repo on this server will be gitolite:reponame.git From 90e141cd61e64a66d4189e4b8e6e64e3d5b31aba Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 24 Apr 2010 12:50:54 +0530 Subject: [PATCH 333/850] (rrq) refactor all the rights querying This commit series refactors all the rights querying logic. - old repo_rights sub renamed to wild_repo_rights - new repo_rights sub to be a single entry point for most rights queries - callable from gl-auth-command and expand_wild - callable from *outside* too, as long as $ENV{GL_USER} is set - the format of the returned permissions contains C, R, and W as applicable, with sigils reflecting the 3 possible ways in which you can get R or W perms (2 ways for C): @R means @all users have the same access #R means you're a "super user" (think root's shell prompt) so you can see all repos R is the normal, explicit, access --- src/gitolite.pm | 70 +++++++++++++++++++++++++++++++++++++++++---- src/gl-auth-command | 4 +-- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 3bc513f..e3e7578 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -34,7 +34,7 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple patter our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file -our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS); +our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED); our %repos; # ---------------------------------------------------------------------------- @@ -159,7 +159,7 @@ sub new_repo # ---------------------------------------------------------------------------- # "who created this repo", "am I on the R list", and "am I on the RW list"? -sub repo_rights +sub wild_repo_rights { my ($repo_base_abs, $repo, $user) = @_; # creater @@ -189,7 +189,7 @@ sub repo_rights sub get_set_perms { my($repo_base_abs, $repo, $verb, $user) = @_; - my ($creater, $dummy, $dummy2) = &repo_rights($repo_base_abs, $repo, ""); + my ($creater, $dummy, $dummy2) = &wild_repo_rights($repo_base_abs, $repo, ""); die "$repo doesnt exist or is not yours\n" unless $user eq $creater; wrap_chdir("$repo_base_abs"); wrap_chdir("$repo.git"); @@ -209,7 +209,7 @@ sub get_set_perms sub get_set_desc { my($repo_base_abs, $repo, $verb, $user) = @_; - my ($creater, $dummy, $dummy2) = &repo_rights($repo_base_abs, $repo, ""); + my ($creater, $dummy, $dummy2) = &wild_repo_rights($repo_base_abs, $repo, ""); die "$repo doesnt exist or is not yours\n" unless $user eq $creater; wrap_chdir("$repo_base_abs"); wrap_chdir("$repo.git"); @@ -344,7 +344,7 @@ sub expand_wild } else { # find the creater and subsitute in repos my ($read, $write); - ($creater, $read, $write) = &repo_rights($repo_base_abs, $actual_repo, $user); + ($creater, $read, $write) = &wild_repo_rights($repo_base_abs, $actual_repo, $user); # get access list with this &parse_acl($GL_CONF_COMPILED, $actual_repo, $creater, $read || "NOBODY", $write || "NOBODY"); $creater = "($creater)"; @@ -358,6 +358,66 @@ sub expand_wild } } +# there will be multiple calls to repo_rights; better to use a closure. We +# might even be called from outside (see the admin-defined-commands docs for +# how/why). Regardless of how we're called, we assume $ENV{GL_USER} is +# already defined +{ + my %normal_repos; + + sub repo_rights { + my $repo = shift; + $repo =~ s/^\.\///; + $repo =~ s/\.git$//; + + # we get passed an actual repo name. It may be a normal + # (non-wildcard) repo, in which case it is assumed to exist. If it's + # a wildrepo, it may or may not exist. If it doesn't exist, the "C" + # perms are also filled in, else that column is left blank + + unless (%normal_repos) { + unless ($REPO_BASE) { + # means we've been called from outside + &where_is_rc(); + die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; + } + + &parse_acl($GL_CONF_COMPILED, "", "NOBODY", "NOBODY", "NOBODY"); + %normal_repos = %repos; + } + + my $creater; + my $perm = ' '; + + # if repo is present "as is" in the config, those permissions will + # override anything inherited from a wildcard that may have matched + if ($normal_repos{$repo}) { + %repos = %normal_repos; + $creater = ''; + } elsif ( -d "$ENV{GL_REPO_BASE_ABS}/$repo.git" ) { + # must be a wildrepo, and it has already been created; find the + # creater and subsitute in repos + my ($read, $write); + ($creater, $read, $write) = &wild_repo_rights($ENV{GL_REPO_BASE_ABS}, $repo, $ENV{GL_USER}); + # get access list with these substitutions + &parse_acl($GL_CONF_COMPILED, $repo, $creater || "NOBODY", $read || "NOBODY", $write || "NOBODY"); + $creater = "($creater)"; + } else { + # repo didn't exist; C perms also need to be filled in after + # getting access list with only creater filled in + &parse_acl($GL_CONF_COMPILED, $repo, $ENV{GL_USER}, "NOBODY", "NOBODY"); + $perm = ( $repos{$repo}{C}{'@all'} ? ' @C' : ( $repos{$repo}{C}{$ENV{GL_USER}} ? ' =C' : ' ' )) if $GL_WILDREPOS; + # if you didn't have perms to create it, delete the "convenience" + # copy of the ACL that parse_acl makes + delete $repos{$repo} unless $perm =~ /C/; + $creater = ""; + } + $perm .= ( $repos{$repo}{R}{'@all'} ? ' @R' : ( $repos{'@all'}{R}{$ENV{GL_USER}} ? ' #R' : ( $repos{$repo}{R}{$ENV{GL_USER}} ? ' R' : ' ' ))); + $perm .= ( $repos{$repo}{W}{'@all'} ? ' @W' : ( $repos{'@all'}{W}{$ENV{GL_USER}} ? ' #W' : ( $repos{$repo}{W}{$ENV{GL_USER}} ? ' W' : ' ' ))); + return($perm, $creater); + } +} + # ---------------------------------------------------------------------------- # S P E C I A L C O M M A N D S # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index 24fc5b9..0621a43 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -49,7 +49,7 @@ $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; # set the umask before creating any files umask($REPO_UMASK); -my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +my $repo_base_abs = $ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); # ---------------------------------------------------------------------------- # start... @@ -170,7 +170,7 @@ $ENV{GL_REPO}=$repo; if ( -d "$repo_base_abs/$repo.git" ) { # existing repo - my ($creater, $user_R, $user_W) = &repo_rights($repo_base_abs, $repo, $user); + my ($creater, $user_R, $user_W) = &wild_repo_rights($repo_base_abs, $repo, $user); &parse_acl($GL_CONF_COMPILED, $repo, $creater, $user_R, $user_W); } else { &parse_acl($GL_CONF_COMPILED, $repo, $user, "NOBODY", "NOBODY"); From 6be0946aee6573360bf74c0aea409bf7c08a0a4f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 24 Apr 2010 13:14:16 +0530 Subject: [PATCH 334/850] (rrq) gl-auth-command uses new repo_rights sub --- src/gl-auth-command | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 0621a43..1192916 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -168,32 +168,18 @@ $ENV{GL_REPO}=$repo; # first level permissions check # ---------------------------------------------------------------------------- -if ( -d "$repo_base_abs/$repo.git" ) { - # existing repo - my ($creater, $user_R, $user_W) = &wild_repo_rights($repo_base_abs, $repo, $user); - &parse_acl($GL_CONF_COMPILED, $repo, $creater, $user_R, $user_W); -} else { - &parse_acl($GL_CONF_COMPILED, $repo, $user, "NOBODY", "NOBODY"); - - # auto-vivify new repo if you have C access (and wildrepos is on) - if ( $GL_WILDREPOS and $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'} ) { - wrap_chdir("$repo_base_abs"); - new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); - wrap_chdir($ENV{HOME}); - } else { - # repo didn't exist, and you didn't have perms to create it. Delete - # the "convenience" copy of the ACL that parse_acl makes for us - delete $repos{$repo}; - } +my ($perm, $creater) = &repo_rights($repo); +if ($perm =~ /C/) { + # it was missing, and you have create perms + wrap_chdir("$repo_base_abs"); + new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); + wrap_chdir($ENV{HOME}); } # we know the user and repo; we just need to know what perm he's trying -my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W'); - -die "$perm access for $repo DENIED to $user\n" - unless $repos{$repo}{$perm}{$user} - or $repos{'@all'}{$perm}{$user} # new: access to @all repos - or $repos{$repo}{$perm}{'@all'}; +# aa == attempted access +my $aa = ($verb =~ $R_COMMANDS ? 'R' : 'W'); +die "$aa access for $repo DENIED to $user\n" unless $perm =~ /$aa/; # ---------------------------------------------------------------------------- # over to git now From 382505f0e0de70d24b072d1822c0c0e69745b74b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 24 Apr 2010 13:17:37 +0530 Subject: [PATCH 335/850] (rrq) expand_wild uses new repo_rights sub --- src/gitolite.pm | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index e3e7578..e088fd6 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -320,10 +320,6 @@ sub expand_wild # access report instead of having to manually change CREATER to his name $repo =~ s/\bCREAT[EO]R\b/$user/g; - # get the list of repo patterns - &parse_acl($GL_CONF_COMPILED, "", "NOBODY", "NOBODY", "NOBODY"); - my %normal_repos = %repos; - # display matching repos (from *all* the repos in the system) that $user # has at least "R" access to @@ -334,26 +330,9 @@ sub expand_wild $actual_repo =~ s/\.git$//; # actual_repo has to match the pattern being expanded next unless $actual_repo =~ /$repo/; - # if actual_repo is present "as is" in the config, those - # permissions will override anything inherited from a - # wildcard that also happens to match - my $creater; - if ($normal_repos{$actual_repo}) { - %repos = %normal_repos; - $creater = ''; - } else { - # find the creater and subsitute in repos - my ($read, $write); - ($creater, $read, $write) = &wild_repo_rights($repo_base_abs, $actual_repo, $user); - # get access list with this - &parse_acl($GL_CONF_COMPILED, $actual_repo, $creater, $read || "NOBODY", $write || "NOBODY"); - $creater = "($creater)"; - } - my $perm = ' '; - # @all repos; see notes above - $perm .= ( $repos{$actual_repo}{R}{'@all'} ? ' @' : ( $repos{'@all'}{R}{$user} ? ' r' : ( $repos{$actual_repo}{R}{$user} ? ' R' : ' ' ))); - $perm .= ( $repos{$actual_repo}{W}{'@all'} ? ' @' : ( $repos{'@all'}{W}{$user} ? ' w' : ( $repos{$actual_repo}{W}{$user} ? ' W' : ' ' ))); - next if $perm eq ' '; + + my($perm, $creater) = &repo_rights($actual_repo); + next unless $perm =~ /\S/; print "$perm\t$creater\t$actual_repo\n"; } } From 08dced612552cf4215629feb0006c06cdbb494a0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 24 Apr 2010 14:54:31 +0530 Subject: [PATCH 336/850] (rrq) report_basic adjusts output format to match expand_wild --- src/gitolite.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index e088fd6..4a4cbae 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -296,12 +296,12 @@ sub report_basic print "\rthe gitolite config gives you the following access:\r\n"; for my $r (sort keys %repos) { # @all repos; meaning of read/write flags: - # @ => @all users are allowed access to this repo - # r/w => you are allowed access to @all repos - # R/W => you are allowed access to this repo - my $perm .= ( $repos{$r}{C}{'@all'} ? ' @' : ( $repos{$r}{C}{$user} ? ' C' : ' ' ) ); - $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{'@all'}{R}{$user} ? ' r' : ( $repos{$r}{R}{$user} ? ' R' : ' ' ))); - $perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{'@all'}{W}{$user} ? ' w' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ))); + # @R => @all users are allowed access to this repo + # #R => you're a super user and can see @all repos + # R => normal access + my $perm .= ( $repos{$r}{C}{'@all'} ? ' @C' : ( $repos{$r}{C}{$user} ? ' C' : ' ' ) ); + $perm .= ( $repos{$r}{R}{'@all'} ? ' @R' : ( $repos{'@all'}{R}{$user} ? ' #R' : ( $repos{$r}{R}{$user} ? ' R' : ' ' ))); + $perm .= ( $repos{$r}{W}{'@all'} ? ' @W' : ( $repos{'@all'}{W}{$user} ? ' #W' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ))); print "$perm\t$r\r\n" if $perm =~ /\S/; } } From 6edc7a4d5fbeccc9dbda5c06181bfa2e5ed1f47c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 24 Apr 2010 15:46:25 +0530 Subject: [PATCH 337/850] (rrq) document the report output --- doc/3-faq-tips-etc.mkd | 26 +++++++++++++------------- doc/report-output.mkd | 23 ++++------------------- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index ff8dc09..a393998 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -408,20 +408,20 @@ etc. You'd just like a simple way to know what repos you have access to. Easy! Just use ssh to give the "info" command to the gitolite server: $ ssh git@server info - hello sitaram, the gitolite version here is v1.4-9-g34aad34 + hello sitaram, the gitolite version here is v1.4.2-4-g40cbecd the gitolite config gives you the following access: - R W SecureBrowse - R W anu-wsd - R W entrans - @ W git-notes - @ W gitolite - R W gitolite-admin - R W indic_web_input - @ R private/CREATER/[\w.-]+ - R W proxy - @ @ W public/CREATER/[\w.-]+ - @ @ testing - R W vkc + #R W SecureBrowse + #R W anu-wsd + #R W entrans + @R W git-notes + @R W gitolite + #R W gitolite-admin + #R W indic_web_input + @C #R private/CREATER/[\w.-]+ + #R W proxy + @C @R W public/CREATER/[\w.-]+ + @R @W testing + #R W vkc To understand what these symbols mean, please see doc/report-output.mkd. The administrator can also say things like: diff --git a/doc/report-output.mkd b/doc/report-output.mkd index a0dd87d..b76ebab 100644 --- a/doc/report-output.mkd +++ b/doc/report-output.mkd @@ -22,17 +22,10 @@ can append a list of users to see their permissions instead of your own. (Side note: if you installed using easy-install that would probably be `ssh gitolite info`, by the way). - * col 1 (exists only if the "repo" name is actually a pattern) - * `@`: `@all` users have permission to create repos matching this - pattern - * `C`: this user has permission to create repos matching this pattern - - * col 2 - * `@`: `@all` users have read access to this repo - * `r`: this user has read access to `@all` repos - * `R`: this user has read access to this repo - - * col 3: same as col 2, but for write access instead of read +The meaning of C, R, and W are self-explanatory, but they might sometimes be +prefixed by a symbol. For example, `@R` means that `@all` users have +beengiven this access, and `#R` means that this user is a "superuser" (think +root's shell prompt) and so has access to `@all` repos. ### the "expand" command @@ -51,11 +44,3 @@ account extra permissions enabled by the `setperms` command (see doc/4-wildcard-repositories.mkd). It shows you the "creater" of the repo as an additional column, defaulting to `` if it was not a wildcard repo. - - * col 1: not used - - * col 2: same as col 2 for info, except substitute "repo" with "repo or - wildcard matching this repo" - - * col 3: same as col 2, but for write access instead of read - From 567e70ba40a9bd5c9ebea530c5d899d1120f27c3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 24 Apr 2010 18:16:13 +0530 Subject: [PATCH 338/850] (adc) admin-defined commands This commit series allows an admin to designate a set of commands that users can run. For example, he can allow users to delete a repo that they have created: ssh git@server rmrepo foo/me/bar or fork (to use github's terminology) a repo they have "R" access to, into a new one they have "C" access to: ssh git@server fork foo/someone-else/bar foo/me/bar Please see documentation for details ---- (this commit) - (rc) new variable $GL_ADC_PATH; without this none of this is enabled - (pm) new helper routine "cli_repo_rights" to get rights/ownership from outside - (auth) call $GL_ADC_PATH/$cmd if it exists --- conf/example.gitolite.rc | 9 +++++++++ src/gitolite.pm | 8 ++++++++ src/gl-auth-command | 16 +++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index b1a921f..5568055 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -170,6 +170,15 @@ $GL_WILDREPOS = 0; # $UPDATE_CHAINS_TO = "hooks/update.secondary"; # $ADMIN_POST_UPDATE_CHAINS_TO = "hooks/post-update.secondary"; +# -------------------------------------- +# ADMIN DEFINED COMMANDS + +# WARNING: Use this feature only if (a) you really really know what you're +# doing or (b) you really don't care too much about security. Please read +# doc/admin-defined-commands.mkd for details. + +# $GL_ADC_PATH = ""; + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/src/gitolite.pm b/src/gitolite.pm index 4a4cbae..a49ffa8 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -397,6 +397,14 @@ sub expand_wild } } +# helper/convenience routine to get rights and ownership from a shell command +sub cli_repo_rights { + my ($perm, $creater) = &repo_rights($_[0]); + $perm =~ s/ /_/g; + $creater =~ s/^\(|\)$//g; + print "$perm $creater\n"; +} + # ---------------------------------------------------------------------------- # S P E C I A L C O M M A N D S # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index 1192916..5a3a894 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -24,7 +24,7 @@ use warnings; # ---------------------------------------------------------------------------- # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_ADC_PATH); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; @@ -100,6 +100,20 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { $ENV{SSH_ORIGINAL_COMMAND} = 'info'; } +# ---------------------------------------------------------------------------- +# admin defined commands +# ---------------------------------------------------------------------------- + +# please see doc/admin-defined-commands.mkd for details +if ($GL_ADC_PATH and -d $GL_ADC_PATH) { + my ($cmd, @args) = split ' ', $ENV{SSH_ORIGINAL_COMMAND}; + if (-x "$GL_ADC_PATH/$cmd") { + # yes this is rather strict, sorry. + do { die "I don't like $_\n" unless $_ =~ $REPOPATT_PATT } for ($cmd, @args); + exec("$GL_ADC_PATH/$cmd", @args); + } +} + # ---------------------------------------------------------------------------- # get and set perms for actual repo created by wildcard-autoviv # ---------------------------------------------------------------------------- From 9d0a208b2608be4f677d0ba21bde7780bd39b963 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Apr 2010 04:55:43 +0530 Subject: [PATCH 339/850] (adc) auth: git-init subcommand added The "fork" adc cannot simply do a "git clone..."; hooks and gl-creater won't get set up. We need a way to initiate the *creation* of a repo from a shell command, and then fetch the refs over. For a long time, we used to trick gitolite into creating a repo for us by simply using "git ls-remote host:reponame" ;-) Now we have an actual command, so we can say "ssh git@server git-init \'reponame\'" Yes; those single quotes are required. Deal with it. --- src/gl-auth-command | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 5a3a894..af46ab2 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -163,7 +163,7 @@ if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) { # git-receive-pack 'reponame.git' my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/); -unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) { +unless ( $verb and ( $verb eq 'git-init' or $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) { # ok, it's not a normal git command; call the special command helper &special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE); exit; @@ -202,4 +202,4 @@ die "$aa access for $repo DENIED to $user\n" unless $perm =~ /$aa/; &log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n"); $repo = "'$REPO_BASE/$repo.git'"; -exec("git", "shell", "-c", "$verb $repo"); +exec("git", "shell", "-c", "$verb $repo") unless $verb eq 'git-init'; From 0b1d31fad435adf23df443109a4ada7c50488003 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 24 Apr 2010 23:00:38 +0530 Subject: [PATCH 340/850] (adc) "common functions" and examples for adc usage --- contrib/adc/adc.common-functions | 17 +++++++++++++++++ contrib/adc/fork | 22 ++++++++++++++++++++++ contrib/adc/restrict-admin | 11 +++++++++++ contrib/adc/rmrepo | 11 +++++++++++ 4 files changed, 61 insertions(+) create mode 100644 contrib/adc/adc.common-functions create mode 100755 contrib/adc/fork create mode 100755 contrib/adc/restrict-admin create mode 100755 contrib/adc/rmrepo diff --git a/contrib/adc/adc.common-functions b/contrib/adc/adc.common-functions new file mode 100644 index 0000000..c67710a --- /dev/null +++ b/contrib/adc/adc.common-functions @@ -0,0 +1,17 @@ +#!/bin/bash + +# please make sure this file is NOT chmod +x + +die() { echo "$@"; exit 1; } + +get_rights_and_owner() { + local ans + ans=$(perl -I$HOME/.gitolite/src -Mgitolite -e 'cli_repo_rights("'$1'")') + + # set shell variables as needed + owner=${ans#* } + rights=${ans% *} + [[ $rights =~ C ]] && perm_create=yes || perm_create= + [[ $rights =~ R ]] && perm_read=yes || perm_read= + [[ $rights =~ W ]] && perm_write=yes || perm_write= +} diff --git a/contrib/adc/fork b/contrib/adc/fork new file mode 100755 index 0000000..758675f --- /dev/null +++ b/contrib/adc/fork @@ -0,0 +1,22 @@ +#!/bin/bash + +from=$1 +to=$2 + +. $(dirname $0)/adc.common-functions + +# we cannot just say "cd $GL_REPO_BASE_ABS; git clone $from.git $to.git". That +# won't set up the hooks or the gl-creater file that gitolite needs. So we now +# have a new "git-init" command! + +get_rights_and_owner $from +[[ -z $perm_read ]] && die "no read permissions on $from" + +get_rights_and_owner $to +[[ -z $perm_create ]] && die "no create permissions on $to" + +# let gitolite create the repo first +SSH_ORIGINAL_COMMAND="git-init '$to'" $GL_BINDIR/gl-auth-command $GL_USER +# then copy the refs from $from +cd $GL_REPO_BASE_ABS/$to.git +git fetch $GL_REPO_BASE_ABS/$from.git refs/*:refs/* diff --git a/contrib/adc/restrict-admin b/contrib/adc/restrict-admin new file mode 100755 index 0000000..97ad0d1 --- /dev/null +++ b/contrib/adc/restrict-admin @@ -0,0 +1,11 @@ +#!/bin/bash + +. $(dirname $0)/adc.common-functions + +get_rights_and_owner gitolite-admin +[[ -z $perm_write ]] && die "just *what* are you trying to pull, young man?" + +# and here you let them do the dangerous stuff +echo "+rm -rf $GL_REPO_BASE_ABS" +sleep 2 +echo ...just kidding! diff --git a/contrib/adc/rmrepo b/contrib/adc/rmrepo new file mode 100755 index 0000000..fa2554f --- /dev/null +++ b/contrib/adc/rmrepo @@ -0,0 +1,11 @@ +#!/bin/bash + +. $(dirname $0)/adc.common-functions + +delete=$1 + +get_rights_and_owner $delete +[[ $owner == $GL_USER ]] || die "$delete is not yours to delete!" + +cd $GL_REPO_BASE_ABS +rm -rf $delete.git From 30bfeb881046db35a7e96e0678e6c6d34eca8c37 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Apr 2010 05:49:00 +0530 Subject: [PATCH 341/850] (adc) clean up and extend logging --- src/gitolite.pm | 3 ++- src/gl-auth-command | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index a49ffa8..2429c2b 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -434,6 +434,7 @@ sub special_cmd &ext_cmd_rsync($GL_CONF_COMPILED, $RSYNC_BASE, $cmd); } else { # if the user is allowed a shell, just run the command + &log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{GL_USER}\n"); exec $ENV{SHELL}, "-c", $cmd if $shell_allowed; die "bad command: $cmd\n"; @@ -496,7 +497,7 @@ sub ext_cmd_rsync # that should "die" if there's a problem wrap_chdir($RSYNC_BASE); - &log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{USER}\n"); + &log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{GL_USER}\n"); exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND}; } diff --git a/src/gl-auth-command b/src/gl-auth-command index af46ab2..aed1de3 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -94,6 +94,7 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { if ($shell_allowed) { my $shell = $ENV{SHELL}; $shell =~ s/.*\//-/; # change "/bin/bash" to "-bash" + &log_it("$ENV{GL_TS}\t$shell\t$user\n"); exec { $ENV{SHELL} } $shell; } # otherwise, pretend he typed in "info" and carry on... @@ -110,6 +111,7 @@ if ($GL_ADC_PATH and -d $GL_ADC_PATH) { if (-x "$GL_ADC_PATH/$cmd") { # yes this is rather strict, sorry. do { die "I don't like $_\n" unless $_ =~ $REPOPATT_PATT } for ($cmd, @args); + &log_it("$ENV{GL_TS}\t$GL_ADC_PATH/$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{GL_USER}\n"); exec("$GL_ADC_PATH/$cmd", @args); } } @@ -199,7 +201,7 @@ die "$aa access for $repo DENIED to $user\n" unless $perm =~ /$aa/; # over to git now # ---------------------------------------------------------------------------- -&log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n"); +&log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{GL_USER}\n"); $repo = "'$REPO_BASE/$repo.git'"; exec("git", "shell", "-c", "$verb $repo") unless $verb eq 'git-init'; From ff2be4c1edb8988d0b7453d03ed35f5447b9d720 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 24 Apr 2010 22:39:28 +0530 Subject: [PATCH 342/850] (adc) documentation --- doc/admin-defined-commands.mkd | 192 +++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 doc/admin-defined-commands.mkd diff --git a/doc/admin-defined-commands.mkd b/doc/admin-defined-commands.mkd new file mode 100644 index 0000000..da7824a --- /dev/null +++ b/doc/admin-defined-commands.mkd @@ -0,0 +1,192 @@ +# admin defined commands + +**WARNING: Use this feature only if you really really know what you're doing. +If you come back to me saying this feature caused a problem, I will only +laugh. The worse the problem, the louder I will laugh. You won't actually +hear me laughing, but you'll feel it in your bones, trust me!** + +There may be other such **WARNING** sections below. **Read all of them**. + +---- + +In this document: + + * background + * setting it up + * anatomy of a command + * example uses and sample commands in contrib + * fork + * rmrepo + * (bonus) restricted admin + +---- + +### background + +Gitolite was named to be short for "gitosis-lite". Someone now wants to turn +it into a "github-lite" :-) and even had some code to start me off thinking. + +Since my first impulse on being asked for a feature is to say no, I was +casting about for a reason when he gave me one: he first made some noises +about perl, then said something about rewriting it all in scheme. Nice... I +resisted the urge to point him to [this][xkcd224], told him that's a great +idea and he should go for it, mentally blessing him for letting me off the +hook on coding it ;-) [Laziness][lazy] *is* the first virtue you know! + +[xkcd224]: http://xkcd.com/224/ +[lazy]: http://c2.com/cgi/wiki?LazinessImpatienceHubris + +And that was that. For a couple of days. + +Soon, though, I realised that there could be a pretty big bonus in this for +tightly controlled setups, so I went and coded it all anyway. See the section +on "restricted admin" for what's really exciting about this for *me*. + +---- + +It may be a good idea to read [doc/4-wildcard-repositories.mkd][wild] before +you continue here though, because this feature builds on top of that. + +[wild]: http://github.com/sitaramc/gitolite/blob/pu/doc/4-wildcard-repositories.mkd + +The wildcard repo feature is a way to create repositories matching a pattern +(even if it as simple as `personal/CREATER/.+`), and a way to specify two +categories of permissions for each such user-created repo. + +What we want now is more than that, as you'll see in the examples below. And +I'm sure if you think of more uses you'll send them to me as "contrib" +entries, right? + +### setting it up + +This can only be setup by someone who has shell access to the server. Edit +the rc file and update the `$GL_ADC_PATH` variable to point to, say, +`/home/git/bin/adc`. *Nothing happens unless this variable is set and +pointing to a directory*. Then put in whatever such commands you create into +that directory. If you have a command called "foo" in that directory, then a +user can invoke it by saying: + + ssh git@server foo argument list + +**WARNING: When gitolite takes control, this directory is checked first, and +if the requested command exists, it is executed. It is therefore quite easy +to inadvertently *hide* some of the "official" commands (like "info", +"expand", "setperms", etc., or worse, say "git-upload-pack"!) by creating +executable files with those names in this directory. So don't do that -- you +have been warned!** + +### anatomy of a command + +You can basically do whatever you want in such a command -- go wild! It's +upto you to check the permissions of *each* repo that the user is manipulating +using your command -- you can `rm -rf $GL_REPO_BASE_ABS` if you like and +gitolite wouldn't stop you. + +The current directory (`$PWD`) will be set to the `$HOME` of `git@server` (or +whatever id you're using). It won't be any specific repo, it won't even be +the base directory of all the repos. + +Gitolite defines a few environment variables, as well as allows you to +directly query the ownership and access permissions of any repository. + +The environment variables available are: + + * `GL_USER` -- the name of the user invoking the command + * `GL_BINDIR` -- the directory containing all the binaries (in particular, + `gitolite.pm`, which is all we really care about here) + * `GL_REPO_BASE_ABS` -- the absolute path of the base directory containing + all the repos + +There are a few other variables also available but the above are the only ones +you should rely on. Please treat any other variables you notice as being +internal/undocumented/subject to change. + +[Implementation note: some of the distro packagers don't seem to like +`GL_BINDIR`. I have not tested this in those scenarios, but they probably put +`gitolite.pm` somewhere in perl's lib path anyway, so it ought to work]. + +In addition, all the arguments of the command are also available to you, so +you can define your own command syntaxes. Gitolite checks these arguments to +make sure they fit a somewhat conservative pattern (see `$REPOPATT_PATT` in +`src/gitolite.pm`), so take that into consideration when designing your +commands and usage. + +Finally, you can call gitolite to query ownership and permissions for the +current user (which may not necessarily be the owner). This is done loosely +as follows (don't use this exact code yet though): + + perl -I$HOME/.gitolite/src -Mgitolite -e "cli_repo_rights('reponame')" + +which will print two space-separated words, something like `_____R__W u1` or +maybe `____@R_@W `. (The former is the response for a wildcard repo +created by user `u1`, the latter is for a non-wildcard repo) + +But that's cumbersome. There's a bash shell function called +`get_rights_and_owner` in `contrib/adc/adc.common-functions` that is much more +convenient. See any of the other samples for how to use it. + +If you don't like this, roll your own. If you don't like bash, do the eqvt in +your language of choice. + +### example uses and sample commands in contrib + +#### fork + +A user would use the fork command like this: + + ssh git@server fork from to + +where "from" is a repo to which the user invoking the fork has "R" access, and +"to" is a repo that does not yet exist and to which he has "C" access. + +(Reminder: these access checks are done by the "fork" script, **not** within +gitolite -- once again, you are responsible for making sure your scripts +maintain the security of the system!) + +Strictly speaking this command is not really needed. Even without all this +"admin-defined commands" setup you could still do the following, purely from +the client side: + + git clone git@server:from + cd from + git remote add new git@server:to + git push new refs/*:refs/* + +or some such incantation. + +**Implementation note**: you might notice that this script does not just do a +"git clone ...". This is because creating a repo has to be done via gitolite. +Otherwise, hooks won't get setup, and the all-important "gl-creater" file +showing who owns the repo won't get created. + +So now we have an actual command to just create a repo and do nothing else: +`ssh git@server git-init \'reponame\'`. [Yes; those single quotes are +required. Deal with it.] + +#### rmrepo + +This is one thing that you really could not do before this setup was created. +Use it like this: + + ssh git@server rmrepo reponame + +The script checks to make sure that the repo being deleted was *created* by +the user invoking it. + +#### (bonus) restricted admin + +It's rather important to me (and presumably others in the "corporate" world) +to separate permission to push to the "gitolite-admin" repo from unrestricted +shell access to the server. This issue has been visited often in the past. + +Until now, though, this was binary -- you either had full shell access or none +at all. If there were tasks that legitimately needed to be done from the +shell on the server, it often meant you had to break that separation or load +the few people who did have shell access already. + +Now, however, it is possible to provide scripts to do what you want, and put +them in `$GL_ADC_PATH`. `contrib/adc/restrict-admin` is a commented sample -- +as you can see, it cleverly makes use of the fact that you can now check for +the invoking uses access to any repo in the system. In this case it checks if +he has "W" access to the gitolite-admin repo, and if he does, allows the +script to proceed. From c4cbfabd4c70d6a057a9851a68fc77f2e86f070c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Apr 2010 22:39:27 +0530 Subject: [PATCH 343/850] spelling cluestick... Ouch! How mortifying :) I'd always thought this was one of the Brit/US differences, but to find out that it really *isn't* a word... hmph! Anyway, in the interest of not breaking existing wild repos, the ownership file is still called "gl-creater". Everything else has been changed. (...thanks to Sverre) --- doc/3-faq-tips-etc.mkd | 4 +-- doc/4-wildcard-repositories.mkd | 32 +++++++++--------- doc/admin-defined-commands.mkd | 2 +- doc/report-output.mkd | 2 +- hooks/common/update | 4 +-- src/gitolite.pm | 58 ++++++++++++++++----------------- src/gl-auth-command | 2 +- src/gl-compile-conf | 10 +++--- 8 files changed, 57 insertions(+), 57 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index a393998..adcf94a 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -417,9 +417,9 @@ Easy! Just use ssh to give the "info" command to the gitolite server: @R W gitolite #R W gitolite-admin #R W indic_web_input - @C #R private/CREATER/[\w.-]+ + @C #R private/CREATOR/[\w.-]+ #R W proxy - @C @R W public/CREATER/[\w.-]+ + @C @R W public/CREATOR/[\w.-]+ @R @W testing #R W vkc diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 9b01dcb..ff09da8 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -20,8 +20,8 @@ workarounds I may not have the time to code it right away. In this document: * wildcard repos - * wildcard repos with creater name in them - * wildcard repos without creater name in them + * wildcard repos with creator name in them + * wildcard repos without creator name in them * side-note: line-anchored regexes * contrast with refexes * handing out rights to wildcard-matched repos @@ -40,7 +40,7 @@ Which of these alternatives you choose depends on your needs, and the social aspects of your environment. The first one is a little more rigid, making it harder to make mistakes, and the second is more flexible and trusting. -#### Wildcard repos with creater name in them +#### Wildcard repos with creator name in them Here's an example snippet: @@ -48,9 +48,9 @@ Here's an example snippet: @TAs = u2 u3 @students = u4 u5 u6 - repo assignments/CREATER/a[0-9][0-9] + repo assignments/CREATOR/a[0-9][0-9] C = @students - RW+ = CREATER + RW+ = CREATOR RW = WRITERS @TAs R = READERS @prof @@ -64,14 +64,14 @@ new repo, as user "u4" (a student): Notice the *two* empty repo inits, and the order in which they occur ;-) -#### Wildcard repos without creater name in them +#### Wildcard repos without creator name in them -Here's how the same example would look if you did not want the CREATER's name +Here's how the same example would look if you did not want the CREATOR's name to be part of the actual repo name. repo assignments/a[0-9][0-9] C = @students - RW+ = CREATER + RW+ = CREATOR RW = WRITERS @TAs R = READERS @prof @@ -122,7 +122,7 @@ part of this list*** is controlled by the person who created the repository. The use case is that, although our toy example has only 3 students, in reality there will be a few dozen, but each assignment will be worked on only by a -handful from among those. This allows the creater to take ad hoc sets of +handful from among those. This allows the creator to take ad hoc sets of users from among the actual users in the system, and place them into one of two categories (whose permissions are, in this example, R and RW respectively). In theory you could do the same thing by creating lots of @@ -214,20 +214,20 @@ This section tells you what is happening inside gitolite so you can understand this feature better. Let's use the config example at the beginning of this document: - repo assignments/CREATER/a[0-9][0-9] + repo assignments/CREATOR/a[0-9][0-9] C = @students - RW+ = CREATER + RW+ = CREATOR RW = WRITERS @TAs R = READERS @prof First we find the set of rules to apply. This involves replacing the special -words CREATER, WRITERS, and READERS with appropriate usernames to derive an +words CREATOR, WRITERS, and READERS with appropriate usernames to derive an "effective" ruleset for the repo in question. -For a **new** repo, replace the word CREATER in all repo patterns and rules +For a **new** repo, replace the word CREATOR in all repo patterns and rules with the name of the invoking user. -> (Note: this is why you should never use `C = CREATER`; it becomes `C = +> (Note: this is why you should never use `C = CREATOR`; it becomes `C = > invoking_user`! Unless you really want to allow *all* users to create > repos, you should restrict "C" perms to an actual user or set of users, > like in the examples in this document). @@ -251,8 +251,8 @@ At this point we have an effective ruleset, and the normal access rules (R, RW, etc) apply, with the addition that the invoking user needs "C" access to be able to create a repo. -> (Note: "C" rights do not automatically give the CREATER any other rights; -> they must be specifically given. `RW+ = CREATER` is recommended in most +> (Note: "C" rights do not automatically give the CREATOR any other rights; +> they must be specifically given. `RW+ = CREATOR` is recommended in most > situations, as you can see in our example). Assuming user "u4" trying to push-create a new repo called diff --git a/doc/admin-defined-commands.mkd b/doc/admin-defined-commands.mkd index da7824a..4612c7e 100644 --- a/doc/admin-defined-commands.mkd +++ b/doc/admin-defined-commands.mkd @@ -50,7 +50,7 @@ you continue here though, because this feature builds on top of that. [wild]: http://github.com/sitaramc/gitolite/blob/pu/doc/4-wildcard-repositories.mkd The wildcard repo feature is a way to create repositories matching a pattern -(even if it as simple as `personal/CREATER/.+`), and a way to specify two +(even if it as simple as `personal/CREATOR/.+`), and a way to specify two categories of permissions for each such user-created repo. What we want now is more than that, as you'll see in the examples below. And diff --git a/doc/report-output.mkd b/doc/report-output.mkd index b76ebab..51cb565 100644 --- a/doc/report-output.mkd +++ b/doc/report-output.mkd @@ -41,6 +41,6 @@ For each repo found, it searches for it in the config -- either the actual repo entry (when the repo is not a wildcard repo), or an entry for the wildcard that matches it -- and reports permissions. It also takes into account extra permissions enabled by the `setperms` command (see -doc/4-wildcard-repositories.mkd). It shows you the "creater" of the repo as +doc/4-wildcard-repositories.mkd). It shows you the "creator" of the repo as an additional column, defaulting to `` if it was not a wildcard repo. diff --git a/hooks/common/update b/hooks/common/update index dc87d03..0557c0a 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -35,12 +35,12 @@ exit 0 if exists $ENV{GL_BYPASS_UPDATE_HOOK}; # we should already have the GL_RC env var set when we enter this hook die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; # then "do" the compiled config file, whose name we now know. Before doing -# that we setup the creater etc from environment variables so that the parse +# that we setup the creator etc from environment variables so that the parse # interpolates them. We've minimised the duplication but this *does* # duplicate a bit of parse_acl from gitolite.pm; we don't want to include that # file here just for that little bit { - our $creater = $ENV{GL_CREATER}; + our $creator = $ENV{GL_CREATOR}; our $readers = $ENV{GL_READERS}; our $writers = $ENV{GL_WRITERS}; our $gl_user = $ENV{GL_USER}; diff --git a/src/gitolite.pm b/src/gitolite.pm index 2429c2b..998c894 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -27,7 +27,7 @@ our $W_COMMANDS=qr/^git[ -]receive-pack$/; # note that REPONAME_PATT allows "/", while USERNAME_PATT does not # also, the reason REPONAME_PATT is a superset of USERNAME_PATT is (duh!) -# because in this version, a repo can have "CREATER" in the name (see docs) +# because in this version, a repo can have "CREATOR" in the name (see docs) our $REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$); # very simple pattern our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple pattern # same as REPONAME, plus some common regex metas @@ -132,19 +132,19 @@ sub where_is_rc # NOTE: this sub will change your cwd; caller beware! sub new_repo { - my ($repo, $hooks_dir, $creater) = @_; + my ($repo, $hooks_dir, $creator) = @_; umask($REPO_UMASK); - die "wildrepos disabled, can't set creater $creater on new repo $repo\n" - if $creater and not $GL_WILDREPOS; + die "wildrepos disabled, can't set creator $creator on new repo $repo\n" + if $creator and not $GL_WILDREPOS; system("mkdir", "-p", "$repo.git") and die "$ABRT mkdir $repo.git failed: $!\n"; # erm, note that's "and die" not "or die" as is normal in perl wrap_chdir("$repo.git"); system("git --bare init >&2"); - if ($creater) { - system("echo $creater > gl-creater"); - system("git", "config", "gitweb.owner", $creater); + if ($creator) { + system("echo $creator > gl-creater"); + system("git", "config", "gitweb.owner", $creator); } # propagate our own, plus any local admin-defined, hooks ln_sf($hooks_dir, "*", "hooks"); @@ -162,7 +162,7 @@ sub new_repo sub wild_repo_rights { my ($repo_base_abs, $repo, $user) = @_; - # creater + # creator my $c = ''; if ( -f "$repo_base_abs/$repo.git/gl-creater") { my $fh = wrap_open("<", "$repo_base_abs/$repo.git/gl-creater"); @@ -189,8 +189,8 @@ sub wild_repo_rights sub get_set_perms { my($repo_base_abs, $repo, $verb, $user) = @_; - my ($creater, $dummy, $dummy2) = &wild_repo_rights($repo_base_abs, $repo, ""); - die "$repo doesnt exist or is not yours\n" unless $user eq $creater; + my ($creator, $dummy, $dummy2) = &wild_repo_rights($repo_base_abs, $repo, ""); + die "$repo doesnt exist or is not yours\n" unless $user eq $creator; wrap_chdir("$repo_base_abs"); wrap_chdir("$repo.git"); if ($verb eq 'getperms') { @@ -209,8 +209,8 @@ sub get_set_perms sub get_set_desc { my($repo_base_abs, $repo, $verb, $user) = @_; - my ($creater, $dummy, $dummy2) = &wild_repo_rights($repo_base_abs, $repo, ""); - die "$repo doesnt exist or is not yours\n" unless $user eq $creater; + my ($creator, $dummy, $dummy2) = &wild_repo_rights($repo_base_abs, $repo, ""); + die "$repo doesnt exist or is not yours\n" unless $user eq $creator; wrap_chdir("$repo_base_abs"); wrap_chdir("$repo.git"); if ($verb eq 'getdesc') { @@ -242,7 +242,7 @@ sub parse_acl # And in any case, we set those env vars so level 2 can redo the last # parse without any special code - our $creater = $ENV{GL_CREATER} = $c || $ENV{GL_CREATER} || "NOBODY"; + our $creator = $ENV{GL_CREATOR} = $c || $ENV{GL_CREATOR} || "NOBODY"; our $readers = $ENV{GL_READERS} = $r || $ENV{GL_READERS} || "NOBODY"; our $writers = $ENV{GL_WRITERS} = $w || $ENV{GL_WRITERS} || "NOBODY"; our $gl_user = $ENV{GL_USER}; @@ -289,7 +289,7 @@ sub report_basic { my($GL_ADMINDIR, $GL_CONF_COMPILED, $user) = @_; - &parse_acl($GL_CONF_COMPILED, "", "CREATER", "READERS", "WRITERS"); + &parse_acl($GL_CONF_COMPILED, "", "CREATOR", "READERS", "WRITERS"); # send back some useful info if no command was given &report_version($GL_ADMINDIR, $user); @@ -317,7 +317,7 @@ sub expand_wild &report_version($GL_ADMINDIR, $user); print "\ryou have access to the following repos on the server:\r\n"; # this is for convenience; he can copy-paste the output of the basic - # access report instead of having to manually change CREATER to his name + # access report instead of having to manually change CREATOR to his name $repo =~ s/\bCREAT[EO]R\b/$user/g; # display matching repos (from *all* the repos in the system) that $user @@ -331,9 +331,9 @@ sub expand_wild # actual_repo has to match the pattern being expanded next unless $actual_repo =~ /$repo/; - my($perm, $creater) = &repo_rights($actual_repo); + my($perm, $creator) = &repo_rights($actual_repo); next unless $perm =~ /\S/; - print "$perm\t$creater\t$actual_repo\n"; + print "$perm\t$creator\t$actual_repo\n"; } } @@ -365,44 +365,44 @@ sub expand_wild %normal_repos = %repos; } - my $creater; + my $creator; my $perm = ' '; # if repo is present "as is" in the config, those permissions will # override anything inherited from a wildcard that may have matched if ($normal_repos{$repo}) { %repos = %normal_repos; - $creater = ''; + $creator = ''; } elsif ( -d "$ENV{GL_REPO_BASE_ABS}/$repo.git" ) { # must be a wildrepo, and it has already been created; find the - # creater and subsitute in repos + # creator and subsitute in repos my ($read, $write); - ($creater, $read, $write) = &wild_repo_rights($ENV{GL_REPO_BASE_ABS}, $repo, $ENV{GL_USER}); + ($creator, $read, $write) = &wild_repo_rights($ENV{GL_REPO_BASE_ABS}, $repo, $ENV{GL_USER}); # get access list with these substitutions - &parse_acl($GL_CONF_COMPILED, $repo, $creater || "NOBODY", $read || "NOBODY", $write || "NOBODY"); - $creater = "($creater)"; + &parse_acl($GL_CONF_COMPILED, $repo, $creator || "NOBODY", $read || "NOBODY", $write || "NOBODY"); + $creator = "($creator)"; } else { # repo didn't exist; C perms also need to be filled in after - # getting access list with only creater filled in + # getting access list with only creator filled in &parse_acl($GL_CONF_COMPILED, $repo, $ENV{GL_USER}, "NOBODY", "NOBODY"); $perm = ( $repos{$repo}{C}{'@all'} ? ' @C' : ( $repos{$repo}{C}{$ENV{GL_USER}} ? ' =C' : ' ' )) if $GL_WILDREPOS; # if you didn't have perms to create it, delete the "convenience" # copy of the ACL that parse_acl makes delete $repos{$repo} unless $perm =~ /C/; - $creater = ""; + $creator = ""; } $perm .= ( $repos{$repo}{R}{'@all'} ? ' @R' : ( $repos{'@all'}{R}{$ENV{GL_USER}} ? ' #R' : ( $repos{$repo}{R}{$ENV{GL_USER}} ? ' R' : ' ' ))); $perm .= ( $repos{$repo}{W}{'@all'} ? ' @W' : ( $repos{'@all'}{W}{$ENV{GL_USER}} ? ' #W' : ( $repos{$repo}{W}{$ENV{GL_USER}} ? ' W' : ' ' ))); - return($perm, $creater); + return($perm, $creator); } } # helper/convenience routine to get rights and ownership from a shell command sub cli_repo_rights { - my ($perm, $creater) = &repo_rights($_[0]); + my ($perm, $creator) = &repo_rights($_[0]); $perm =~ s/ /_/g; - $creater =~ s/^\(|\)$//g; - print "$perm $creater\n"; + $creator =~ s/^\(|\)$//g; + print "$perm $creator\n"; } # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index aed1de3..a2f827b 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -184,7 +184,7 @@ $ENV{GL_REPO}=$repo; # first level permissions check # ---------------------------------------------------------------------------- -my ($perm, $creater) = &repo_rights($repo); +my ($perm, $creator) = &repo_rights($repo); if ($perm =~ /C/) { # it was missing, and you have create perms wrap_chdir("$repo_base_abs"); diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 5a81147..2f0d0fd 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -187,7 +187,7 @@ sub parse_conf_file @repos = expand_list ( @repos ); do { die "$ABRT bad reponame $_\n" unless ($GL_WILDREPOS ? $_ =~ $REPOPATT_PATT : $_ =~ $REPONAME_PATT) } for @repos; } - s/\bCREAT[EO]R\b/\$creater/g for @repos; + s/\bCREAT[EO]R\b/\$creator/g for @repos; } # actual permission line elsif (/^(-|C|R|RW\+?D?) (.* )?= (.+)/) @@ -212,7 +212,7 @@ sub parse_conf_file unless (@users == 1 and $users[0] eq '@all'); do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; - s/\bCREAT[EO]R\b/~\$creater/g for @users; + s/\bCREAT[EO]R\b/~\$creator/g for @users; s/\bREADERS\b/\$readers/g for @users; s/\bWRITERS\b/\$writers/g for @users; @@ -358,10 +358,10 @@ for my $fragment_file (glob("conf/fragments/*.conf")) my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); -# the dump uses single quotes, but we convert any strings containing $creater, +# the dump uses single quotes, but we convert any strings containing $creator, # $readers, $writers, to double quoted strings. A wee bit sneaky, but not too # much... -$dumped_data =~ s/'(?=[^']*\$(?:creater|readers|writers|gl_user))~?(.*?)'/"$1"/g; +$dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g; print $compiled_fh $dumped_data; close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; @@ -555,7 +555,7 @@ for my $pubkey (`find . -type f`) # lint check 3; a little more severe than the first two I guess... for my $user (sort keys %user_list) { - next if $user =~ /^(gitweb|daemon|\@all|~\$creater|\$readers|\$writers)$/ or $user_list{$user} eq 'has pubkey'; + next if $user =~ /^(gitweb|daemon|\@all|~\$creator|\$readers|\$writers)$/ or $user_list{$user} eq 'has pubkey'; print STDERR "$WARN user $user in config, but has no pubkey!\n"; } From ffccd0a4d38b2d7213d12257da4bc2299427ff3f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 26 Apr 2010 21:55:02 +0530 Subject: [PATCH 344/850] gl-system-install -- system-wide install program (as if we didn't already have enough programs with the word "install" in their names!) Anyway, this does what an RPM or a DEB would do -- basically implement the instructions in Appendix C of doc/0. You can use this to do a system-wide install if your distro isn't as smart, forward-looking, and uptodate as Fedora ;-) Clone the repo somewhere, cd to it, and run, for example: sudo src/gl-system-install /usr/local/bin /var/gitolite/conf /var/gitolite/hooks or something like that. See doc/0 for details. Run without arguments for help. --- conf/example.gitolite.rc | 7 ++++++ doc/0-INSTALL.mkd | 46 ++++++++++++++++++++++++++-------------- src/gl-install | 2 ++ src/gl-system-install | 45 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 16 deletions(-) create mode 100755 src/gl-system-install diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 5568055..2dee034 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -7,6 +7,13 @@ # You do NOT need to know perl to edit the paths; it should be fairly # self-explanatory and easy to maintain perl syntax :-) +# -------------------------------------- +# Do not uncomment these values unless you know what you're doing +# $GL_PACKAGE_CONF = ""; +# $GL_PACKAGE_HOOKS = ""; + +# -------------------------------------- + # -------------------------------------- # this is where the repos go. If you provide a relative path (not starting diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 59f32dc..5267f69 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -3,8 +3,7 @@ [Update 2009-11-18: easy install now works from msysgit also!] Gitolite is somewhat unusual as far as "server" software goes -- every userid -on the system is a potential "gitolite host" and can install his own version -if he chooses to. +on the server is a potential "gitolite host". This document tells you how to install gitolite. After the install is done, you may want to see the [admin document][admin] for adding users, repos, etc. @@ -63,9 +62,9 @@ The "system install / user setup" section describes this method. ### user install -There is an easy install script that makes installing very easy for the common -case. **This script will setup everything on the server, but you have to run -it on your workstation, NOT on the server!** +The `gl-easy-install` script makes installing very easy for this case. **This +script will setup everything on the server, but you have to run it on your +workstation, NOT on the server!** Assumptions/pre-requisites: @@ -96,6 +95,11 @@ of what it is going to do, and no pauses unless absolutely needed. However, if you're doing this for the first time or you appreciate knowing what it is actually doing, I suggest you skip the `-q`. +A detailed transcript of an install done using this method can be found in +[doc/7-install-transcript.mkd][7it]. + +[7it]: http://github.com/sitaramc/gitolite/blob/pu/doc/7-install-transcript.mkd + #### advantages over the older install methods * all ssh problems reduced to **just one pre-requisite**: enable ssh pubkey @@ -136,21 +140,31 @@ opposed to merely creating one which did not exist) is best left to a human. *don't* have to know perl to do so, it's fairly easy to guess in this limited case. +---- + ### system install / user setup -In this mode a system administrator installs gitolite using the server's -distro package mechanism (yum install, apt-get install, etc). +(Note: other than getting a public key from the admin's workstation, +everything in this mode of install happens on the server). -Once this is done, you as a user must run a command like this (unlike in the -"user install" mode, this is done directly on the server): +In this mode, the software is first installed system-wide, then the user runs +a command to set up the hosting, supplying a public key. - gl-setup yourname.pub +The root user can **install the software system-wide**, using either a package +(rpm, deb, etc.), or by using the `gl-system-install` script. (Run the script +without any arguments for a brief usage message. If that's not clear enough, +read Appendix C below for details.) -where yourname.pub is a copy of a public key from your workstation. The first -time you run this, it will create a "gitolite-admin" repo and populate it with -the right configuration for whoever has the corresponding private key to -clone and push it. In other words, that person is the administrator for this -particular gitolite instance. +A normal user can then **set up the hosting** by running a command like this: + + gl-setup adminname.pub + +where adminname.pub is a copy of a public key from that user's workstation. + +The first time this is run, it will create a "gitolite-admin" repo and +populate it with the right configuration for whoever has the corresponding +private key to clone and push it. In other words, that person is the +administrator for this particular gitolite instance. If your system administrator upgrades gitolite itself, things will usually just work without any change; you should not have to do anything special. @@ -168,7 +182,7 @@ them, expect trouble :-) #### multiple gitolite instances -With this mode of install, it's easy to create multiple gitolite instances +With this mode of install, it's trivial to create multiple gitolite instances (say one for each department, on some mega company-wide server). You can even do this without giving shell access to the admins. Here's an example with just two "departments", and their admins Alice and Bob: diff --git a/src/gl-install b/src/gl-install index f24a0de..b48a525 100755 --- a/src/gl-install +++ b/src/gl-install @@ -1,5 +1,7 @@ #!/usr/bin/perl +# INTERNAL COMMAND. NOT MEANT TO BE RUN BY THE USER DIRECTLY. + use strict; use warnings; diff --git a/src/gl-system-install b/src/gl-system-install new file mode 100755 index 0000000..4da7577 --- /dev/null +++ b/src/gl-system-install @@ -0,0 +1,45 @@ +#!/bin/sh + +# install the gitolite software *system wide*. Not too robust, fancy, etc., +# but does have a usage message and catches simple problems. + +usage() { echo " + Usage: + $0 shared-bin-dir shared-conf-dir shared-hooks-dir + + Example: + $0 /usr/local/bin /var/gitolite/conf /var/gitolite/hooks + + In this example, all the binaries go to /usr/local/bin, and the shared + conf and hooks files/directories go to the other 2 directories given +" + exit 1; +} + +die() { echo "$@"; echo; usage; exit 1; } + +[ -z "$3" ] && usage + +gl_bin_dir=$1 +[ -d $gl_bin_dir ] || die "$gl_bin_dir not found" +gl_conf_dir=$2 +[ -d $gl_conf_dir ] || die "$gl_conf_dir not found" +gl_hooks_dir=$3 +[ -d $gl_hooks_dir ] || die "$gl_hooks_dir not found" + +bindir=`echo $0 | perl -lpe 's/^/$ENV{PWD}\// unless /^\//; s/\/[^\/]+$//;'` +cd $bindir/.. # we assume the standard gitolite source tree is here! + +cp src/* $gl_bin_dir || die "cp src/* to $gl_bin_dir failed" +rm $gl_bin_dir/gl-easy-install +perl -lpi -e "s(^GL_PACKAGE_CONF=.*)(GL_PACKAGE_CONF=$gl_conf_dir)" $gl_bin_dir/gl-setup + +cp -R conf/* $gl_conf_dir || die "cp conf/* to $gl_conf_dir failed" +perl -lpi \ + -e "s(^#\s*\\\$GL_PACKAGE_CONF\s*=.*)(\\\$GL_PACKAGE_CONF = '$gl_conf_dir';)" \ + $gl_conf_dir/example.gitolite.rc +perl -lpi \ + -e "s(^#\s*\\\$GL_PACKAGE_HOOKS\s*=.*)(\\\$GL_PACKAGE_HOOKS = '$gl_hooks_dir';)" \ + $gl_conf_dir/example.gitolite.rc + +cp -R hooks/* $gl_hooks_dir || die "cp hooks/* to $gl_hooks_dir failed" From 6787dc2c84b6044588d50cb2743575b462cb1af0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 27 Apr 2010 10:21:40 +0530 Subject: [PATCH 345/850] (minor) fix docs about admin-ing a gitolite installation (since easy-install is no longer the *only* install method) --- doc/0-INSTALL.mkd | 16 +++++++++++----- doc/2-admin.mkd | 10 +++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 5267f69..b1ae760 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -6,7 +6,7 @@ Gitolite is somewhat unusual as far as "server" software goes -- every userid on the server is a potential "gitolite host". This document tells you how to install gitolite. After the install is done, -you may want to see the [admin document][admin] for adding users, repos, etc. +you may want to see [doc/2-admin.mkd][admin] for adding users, repos, etc. **Please note** that gitolite depends heavily on proper ssh setup and pubkey based access. Sadly, most people don't know ssh as well as they think they @@ -54,7 +54,9 @@ The "user install" section describes this method. The **system-install followed by user-setup** mode is used when you (or someone who has root) has installed an RPM or DEB of gitolite and you intend -to use that version. +to use that version. [Update 2010-04-27: there is now a script called +`gl-system-install` that helps you do a system-install if your distribution +does not yet have a package for gitolite; details below]. The "system install / user setup" section describes this method. @@ -202,11 +204,15 @@ to have a command line on the server, so don't give them the passwords if you don't need to -- the pubkey will allow them to be gitolite admins on their domain, and that's quite enough for normal operations. +---- + ### next steps -The last message produced by the easy install script should tell you how to -add users, repos, etc., and you will find more details in the [admin][admin] -document. +If you installed it using "gl-easy-install", the last message produced by that +script should tell you how to add users, repos, etc. In any case, you will +find more details in [doc/2-admin.mkd][admin]. + +---- diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 4b27d59..8eeaf2a 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -19,7 +19,15 @@ First of all, ***do NOT add new repos manually***, unless you know how to add the required hook as well. Without the hook, branch-level access control will not work for that repo, which sorta defeats the idea of using gitolite :-) -Please read on to see how to do this correctly. +Most normal (day-to-day) gitolite admin work is done by cloning the +gitolite-admin repo from the server to your workstation, making changes to the +clone, and pushing those changes back. + +If you installed using "gl-easy-install", that script would have already tried +to clone the repo to your `$HOME`. Otherwise clone it yourself to any +convenient location you like. + +Once you've cloned it, you're ready to add users and repos. #### adding users and repos From 2eaa2c6416114dda1cdb4610d71d5f90be576ba7 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 27 Apr 2010 14:42:05 +0530 Subject: [PATCH 346/850] (minor) update changelog, features lists in both readme and doc/3 (and raise the reward, though Teemu already wont it actually!) --- README.mkd | 4 +++- doc/3-faq-tips-etc.mkd | 6 ++++++ doc/CHANGELOG | 11 ++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/README.mkd b/README.mkd index ff653ed..b0793d4 100644 --- a/README.mkd +++ b/README.mkd @@ -82,6 +82,8 @@ detail [here][gsdiff]. * "personal namespace" prefix for each dev * migration guide and simple converter for gitosis conf file * "exclude" (or "deny") rights at the branch/tag level + * specify repos using patterns (patterns may include creator's name) + * define powerful operations on the server side, even github-like forking ### security @@ -93,7 +95,7 @@ normal user (not the gitolite admin) to read a repo, or write/rewind a ref, that the config file says he shouldn't, and caused by a bug in *code* that is in the "master" branch, (not in the other branches, or the configuration file or in Unix, perl, shell, etc.)... well I can't afford 1000 USD rewards like -djb, so you'll have to settle for 1000 INR (Indian Rupees) as a "token" prize +djb, so you'll have to settle for 5000 INR (Indian Rupees) as a "token" prize :-) However, there are a few optional features (which must be explicitly enabled diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index adcf94a..bb9c9d6 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -33,6 +33,7 @@ In this document: * easier to link gitweb authorisation with gitolite * advanced features * repos named with wildcards + * admin defined commands * access control for external commands * design choices * keeping the parser and the access control separate @@ -639,6 +640,11 @@ already done and we just use it! Please see `doc/4-wildcard-repositories.mkd` for all the details. +#### admin defined commands + +This requires the wildcards feature to be enabled, but is then an extremely +powerful feature. See `doc/admin-defined-commands.mkd`. + #### access control for external commands Gitolite now has a mechanism for allowing access control for arbitrary diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 32de001..743eeca 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,6 +2,11 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] + - admin-defined commands; see doc/admin-defined-commands.mkd + + - v1.4.2 (prep for major refactor on rights queries + - v1.4.1 (security fix) + - REFUSE TO RUN ON SERVER GIT < 1.6.2 (do NOT upgrade gitolite to or beyond this point if you are unable to upgrade git itself to at least 1.6.2) @@ -33,12 +38,12 @@ the tags can help you position stuff approximately - v1.1 - contrib directory added - - expand now lists non-wildcard repos also (expand is NYD) + - expand now lists non-wildcard repos also - refs also have groups now (NYD) - - allow admins to get "info" for other users (NYD) + - allow admins to get "info" for other users - wildrepos merged - - getdesc and setdesc for wildrepos (NYD) + - getdesc and setdesc for wildrepos - htpasswd subcommand - access control for rsync From 6c682721b5e915247fd1a4d62881b1eb8a057041 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 29 Apr 2010 14:28:03 +0530 Subject: [PATCH 347/850] (minor) doc updates --- contrib/gerrit.mkd | 4 +++- doc/4-wildcard-repositories.mkd | 7 +++++++ doc/admin-defined-commands.mkd | 7 ++++++- src/gl-easy-install | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/contrib/gerrit.mkd b/contrib/gerrit.mkd index f08a1af..679f1a9 100644 --- a/contrib/gerrit.mkd +++ b/contrib/gerrit.mkd @@ -2,7 +2,9 @@ Gerrit and gitolite have too many high level differences. Size is most visible of course: 56000 lines of Java versus 1300 lines of perl+shell, -according to David A. Wheeler's 'SLOCCount' tool. Gerrit needs a database, +according to David A. Wheeler's 'SLOCCount' tool. Gerrit needs a database (it +comes with a perfectly usable one, or I believe you can use any of the usual +suspects), and even comes with its own ssh server and git server, and since the git engine is internal it probably has to include a lot of things that normal git already has; I wouldn't know for sure. diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index ff09da8..9837bfc 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -19,6 +19,7 @@ workarounds I may not have the time to code it right away. In this document: + * rc file setting required * wildcard repos * wildcard repos with creator name in them * wildcard repos without creator name in them @@ -34,6 +35,12 @@ This document is mostly "by example". ---- +### rc file setting required + +This feature requires that you set `$GL_WILDREPOS` to "1" in `~/.gitolite.rc` +on the server. Please search for that variable and see comments around it in +`conf/example.gitolite.rc` for more information on this. + ### Wildcard repos Which of these alternatives you choose depends on your needs, and the social diff --git a/doc/admin-defined-commands.mkd b/doc/admin-defined-commands.mkd index 4612c7e..a1d1b89 100644 --- a/doc/admin-defined-commands.mkd +++ b/doc/admin-defined-commands.mkd @@ -45,7 +45,9 @@ on "restricted admin" for what's really exciting about this for *me*. ---- It may be a good idea to read [doc/4-wildcard-repositories.mkd][wild] before -you continue here though, because this feature builds on top of that. +you continue here though, because most of the uses of this feature also need +wildcard repos. (This also means you must set `$GL_WILDREPOS` to "1" in the +rc file). [wild]: http://github.com/sitaramc/gitolite/blob/pu/doc/4-wildcard-repositories.mkd @@ -190,3 +192,6 @@ as you can see, it cleverly makes use of the fact that you can now check for the invoking uses access to any repo in the system. In this case it checks if he has "W" access to the gitolite-admin repo, and if he does, allows the script to proceed. + +[Note that this particular use does not require `$GL_WILDREPOS` to be enabled, +because it's not using any wildcard repos]. diff --git a/src/gl-easy-install b/src/gl-easy-install index fdb8776..52df88b 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -568,7 +568,7 @@ v_done=" done! IMPORTANT NOTE -- PLEASE READ!!! - *Your* URL for cloning any repo on this server will be + *Your* URL for cloning any repo from this server will be gitolite:reponame.git *Other* users you set up will have to use From f1a942b7f7d7b50422c0ec031e5be168fc6ef5d3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Apr 2010 13:49:19 +0530 Subject: [PATCH 348/850] (minor) more helpful message when the user forgot to set $GL_WILDREPOS thanks to konrad for catching this also make lack of WILDREPOS more noticable on compile --- src/gl-compile-conf | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 2f0d0fd..f12a451 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -185,7 +185,12 @@ sub parse_conf_file @repos = split ' ', $1; unless (@repos == 1 and $repos[0] eq '@all') { @repos = expand_list ( @repos ); - do { die "$ABRT bad reponame $_\n" unless ($GL_WILDREPOS ? $_ =~ $REPOPATT_PATT : $_ =~ $REPONAME_PATT) } for @repos; + for (@repos) { + die "$ABRT bad reponame $_\n" + if ($GL_WILDREPOS and $_ !~ $REPOPATT_PATT); + die "$ABRT bad reponame $_ or you forgot to set \$GL_WILDREPOS\n" + if (not $GL_WILDREPOS and $_ !~ $REPONAME_PATT); + } } s/\bCREAT[EO]R\b/\$creator/g for @repos; } @@ -196,7 +201,7 @@ sub parse_conf_file my @refs; @refs = split(' ', $2) if $2; @refs = expand_list ( @refs ); my @users = split ' ', $3; - die "wildrepos disabled, cant use 'C' in config\n" if $perms eq 'C' and not $GL_WILDREPOS; + die "$ABRT \$GL_WILDREPOS is not set, you cant use 'C' in config\n" if $perms eq 'C' and not $GL_WILDREPOS; # if no ref is given, this PERM applies to all refs @refs = qw(refs/.*) unless @refs; From d95e868620ef345eb69159334453624827f2e6e6 Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Sun, 9 May 2010 18:04:54 +0300 Subject: [PATCH 349/850] add svnserve exec support --- src/gitolite.pm | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/gitolite.pm b/src/gitolite.pm index 3bc513f..b3efd0c 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -35,6 +35,7 @@ our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS); +our ($SVNSERVE); our %repos; # ---------------------------------------------------------------------------- @@ -385,6 +386,8 @@ sub special_cmd &ext_cmd_htpasswd($HTPASSWD_FILE); } elsif ($RSYNC_BASE and $cmd =~ /^rsync /) { &ext_cmd_rsync($GL_CONF_COMPILED, $RSYNC_BASE, $cmd); + } elsif ($SVNSERVE and $cmd eq 'svnserve -t') { + &ext_cmd_svnserve($SVNSERVE); } else { # if the user is allowed a shell, just run the command exec $ENV{SHELL}, "-c", $cmd if $shell_allowed; @@ -480,4 +483,17 @@ EOFhtp die "htpasswd command seems to have failed with $rc return code...\n" if $rc; } +# ---------------------------------------------------------------------------- +# external command helper: svnserve +# ---------------------------------------------------------------------------- + +sub ext_cmd_svnserve +{ + my $SVNSERVE = shift; + + $SVNSERVE =~ s/%u/$ENV{GL_USER}/g; + exec $SVNSERVE; + die "svnserve exec failed\n"; +} + 1; From cf9bb98e87fbeec265a5e981e76750f9fb4a1b44 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 9 May 2010 18:04:55 +0300 Subject: [PATCH 350/850] tweaked and documented svnserve support --- conf/example.gitolite.rc | 14 ++++++++++++++ doc/3-faq-tips-etc.mkd | 12 ++++++++++++ src/gitolite.pm | 3 +-- src/gl-auth-command | 4 ++-- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index b1a921f..91c776b 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -143,6 +143,20 @@ $RSYNC_BASE = ""; # $RSYNC_BASE = "/home/git/up-down"; # $RSYNC_BASE = "/tmp/up-down"; +# -------------------------------------- +# EXTERNAL COMMAND HELPER -- SVNSERVE + +# security note: runs an external command (svnserve) with specific arguments, +# as specified below. %u is substituted with the username. + +# This setting allows launching svnserve when requested by the ssh client. +# This allows using the same SSH setup (hostname/username/public key) for both +# SVN and git access. Leave it undefined or set to the empty string to disable +# svnserve access. + +$SVNSERVE = ""; +# $SVNSERVE = "/usr/bin/svnserve -r /var/svn/ -t --tunnel-user=%u"; + # -------------------------------------- # ALLOW REPO CONFIG TO USE WILDCARDS diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index ff8dc09..cd8336d 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -503,6 +503,18 @@ You can specify hooks that you want to propagate to all repos, as well as per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and `conf/example.conf` for details. + + +#### svnserve #### + +If you are transitioning from SVN to gitolite, and have a lot of users using +public-key authentication with SVN, this feature may be useful to you. Once +you migrate all users' public keys into gitolite, you can set the `$SVNSERVE` +variable in `~/.gitolite.rc` to tie `svnserve` with gitolite's authentication +system. Assuming you installed gitolite to the same user as the one you used +for SVN, SVN connectivity will be retained, and users will be able to use +both SVN and git using the same SSH configuration. + ### helping with gitweb diff --git a/src/gitolite.pm b/src/gitolite.pm index b3efd0c..e7239d3 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -35,7 +35,6 @@ our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS); -our ($SVNSERVE); our %repos; # ---------------------------------------------------------------------------- @@ -365,7 +364,7 @@ sub expand_wild sub special_cmd { - my ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE) = @_; + my ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE, $SVNSERVE) = @_; my $cmd = $ENV{SSH_ORIGINAL_COMMAND}; my $user = $ENV{GL_USER}; diff --git a/src/gl-auth-command b/src/gl-auth-command index 24fc5b9..2cb4371 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -24,7 +24,7 @@ use warnings; # ---------------------------------------------------------------------------- # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $SVNSERVE, $GL_WILDREPOS); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; @@ -151,7 +151,7 @@ if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) { my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/); unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) { # ok, it's not a normal git command; call the special command helper - &special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE); + &special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE, $SVNSERVE); exit; } die "$repo ends with a slash; I don't like that\n" if $repo =~ /\/$/; From 4ad9807225c95074ed8d40e9d84a003ec975feaa Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 10 May 2010 07:25:23 +0530 Subject: [PATCH 351/850] doc/3: made doc for extcmd a little more generic, with specific sections for commands as needed --- doc/3-faq-tips-etc.mkd | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index cd8336d..4dd49b2 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -503,18 +503,6 @@ You can specify hooks that you want to propagate to all repos, as well as per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and `conf/example.conf` for details. - - -#### svnserve #### - -If you are transitioning from SVN to gitolite, and have a lot of users using -public-key authentication with SVN, this feature may be useful to you. Once -you migrate all users' public keys into gitolite, you can set the `$SVNSERVE` -variable in `~/.gitolite.rc` to tie `svnserve` with gitolite's authentication -system. Assuming you installed gitolite to the same user as the one you used -for SVN, SVN connectivity will be retained, and users will be able to use -both SVN and git using the same SSH configuration. - ### helping with gitweb @@ -656,14 +644,34 @@ Please see `doc/4-wildcard-repositories.mkd` for all the details. Gitolite now has a mechanism for allowing access control for arbitrary external commands, as long as they are invoked via ssh and present a server-side command that contains enough information to make an access control -decision. The first (and only, so far) such command implemented is rsync. +decision. Note that this is incompatible with giving people shell access as described in `doc/6-ssh-troubleshooting.mkd` -- people who have shell access are not subject to this mechanism (it wouldn't make sense to try and control someone who has shell access anyway). -Please see the config files (both of them) for examples and usage. +In general, external commands require changes in one or both the config files; +the sample files in `conf/` double as documentation, so you should look there +for examples and usage. + +Commands implemented so far are: + + * rsync + * svnserve (see next section for a brief description; this has been + contributed by Simon and Vladimir) + + + +##### svnserve + +If you are transitioning from SVN to gitolite, and have a lot of users using +public-key authentication with SVN, this feature may be useful to you. Once +you migrate all users' public keys into gitolite, you can set the `$SVNSERVE` +variable in `~/.gitolite.rc` to tie `svnserve` with gitolite's authentication +system. Assuming you installed gitolite to the same user as the one you used +for SVN, SVN connectivity will be retained, and users will be able to use +both SVN and git using the same SSH configuration. ## design choices From fbe785adeaab76ca0c13bd1af9d69831951a9c57 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 14 May 2010 11:26:31 +0530 Subject: [PATCH 352/850] Makefile: now default is as simple as "make" plus copies tar file to /tmp just for convenience --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 7780900..1108b82 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,10 @@ # Note: I'm not sure if that "-r" is a GNU tar extension... +branch := $(shell git rev-parse --abbrev-ref HEAD) + +$(branch): $(branch).tar + .GITOLITE-VERSION: @touch conf/VERSION @@ -14,3 +18,4 @@ git archive $* > $@ tar -r -f $@ conf/VERSION rm conf/VERSION + cp -v $@ /tmp From 9612e3a4cc226ba78ca22ae733f1bd94a098ca28 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 14 May 2010 10:52:58 +0530 Subject: [PATCH 353/850] allow setperms to override config file permissions --- conf/example.gitolite.rc | 19 +++++++++++++++++++ src/gl-compile-conf | 6 ++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 550d6f3..ae4d077 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -110,7 +110,9 @@ $GIT_PATH=""; # You have 3 choices. By default $GL_GITCONFIG_KEYS is left empty, which # completely disables this feature (meaning you cannot set git configs from # the repo config). + $GL_GITCONFIG_KEYS = ""; + # # The second choice is to give it a space separated list of settings you # consider safe. (These are actually treated as a set of regular expression @@ -146,7 +148,9 @@ $HTPASSWD_FILE = ""; # base path of all the files that are accessible via rsync. Must be an # absolute path. Leave it undefined or set to the empty string to disable the # rsync helper. + $RSYNC_BASE = ""; + # $RSYNC_BASE = "/home/git/up-down"; # $RSYNC_BASE = "/tmp/up-down"; @@ -177,8 +181,23 @@ $SVNSERVE = ""; # This has now been rolled into master, with all the functionality gated by # this variable. Set this to 1 if you want to enable the wildrepos features. # Please see doc/4-wildcard-repositories.mkd for details. + $GL_WILDREPOS = 0; +# -------------------------------------- +# ALLOW SETPERMS TO OVERRIDE gitolite.conf + +# A user can be given permissions to use a "wildcard created" repo using the +# "setperms" command run by the creator. However, if that same user is also +# explicitly listed in the config file as having a specific permission, and if +# that permission is different from what was specified using `setperms`, there +# is a conflict. + +# Default behaviour is to let the config file permissions override the +# "setperms" permissions, but if you want it the other way, set this to 1. + +$GL_SETPERMS_OVERRIDES_CONFIG = 0; + # -------------------------------------- # HOOK CHAINING diff --git a/src/gl-compile-conf b/src/gl-compile-conf index f12a451..a6df657 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_SETPERMS_OVERRIDES_CONFIG); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -220,6 +220,8 @@ sub parse_conf_file s/\bCREAT[EO]R\b/~\$creator/g for @users; s/\bREADERS\b/\$readers/g for @users; s/\bWRITERS\b/\$writers/g for @users; + # and double it up if $GL_SETPERMS_OVERRIDES_CONFIG + do { s/\$(creator|readers|writers)\b/~\$$1/g for @users } if $GL_SETPERMS_OVERRIDES_CONFIG; # ok, we can finally populate the %repos hash for my $repo (@repos) # each repo in the current stanza @@ -366,7 +368,7 @@ my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); # the dump uses single quotes, but we convert any strings containing $creator, # $readers, $writers, to double quoted strings. A wee bit sneaky, but not too # much... -$dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g; +$dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~*(.*?)'/"$1"/g; print $compiled_fh $dumped_data; close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; From db3c98d9e3e0e09e24f19aeec3922c85fee2e4ae Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 13 May 2010 10:22:34 +0530 Subject: [PATCH 354/850] easy install: preserve conf/VERSION if working off a tar file --- src/gl-easy-install | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 52df88b..82ec154 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -197,7 +197,12 @@ version_info() { # MANUAL: if needed, make a note of the version you are upgrading from, and to # record which version is being sent across; we assume it's HEAD - git describe --tags --long HEAD 2>/dev/null > conf/VERSION || echo '(unknown)' > conf/VERSION + if git rev-parse --is-inside-work-tree 2>/dev/null + then + git describe --tags --long HEAD 2>/dev/null > conf/VERSION || echo '(unknown)' > conf/VERSION + else + [[ -f conf/VERSION ]] || echo '(unknown)' > conf/VERSION + fi # what was the old version there? export upgrade_details="you are upgrading \ From cf0e568c89bfd1df2e358336b0eedde19ff2632e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 10 May 2010 11:46:47 +0530 Subject: [PATCH 355/850] (big-config) the new "big-config" for large setups If you have many thousands of repos and users, neatly organised into groups, etc., the normal gitolite fails. (It actually runs out of memory very fast while doing the "compile" when you push the config, due to the number of combinations of repo/user being stored in the hash!) This commit series will stop doing that if you set $GL_BIG_CONFIG = 1 in the rc file. Some notes: - deny rules will still work but somewhat differently -- now they must be placed all together in one place to work like before. Ask me for details if you need to know before I get done with the docs - I've tested most of the important features, but not every single nuance - the update hook may be a tad less efficient now; we can try and tweak it later if needed but it shouldn't really hurt anything significantly even now - docs have not been written yet --- conf/example.gitolite.rc | 1 + hooks/common/update | 37 +++----- src/gitolite.pm | 185 ++++++++++++++++++++++++++++++--------- src/gl-auth-command | 7 +- src/gl-compile-conf | 16 ++-- 5 files changed, 170 insertions(+), 76 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index ae4d077..ecaaaa5 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -1,4 +1,5 @@ # paths and configuration variables for gitolite +$GL_BIG_CONFIG = 1; # please read comments before editing diff --git a/hooks/common/update b/hooks/common/update index 0557c0a..f55e2cc 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -34,27 +34,12 @@ exit 0 if exists $ENV{GL_BYPASS_UPDATE_HOOK}; # we should already have the GL_RC env var set when we enter this hook die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; -# then "do" the compiled config file, whose name we now know. Before doing -# that we setup the creator etc from environment variables so that the parse -# interpolates them. We've minimised the duplication but this *does* -# duplicate a bit of parse_acl from gitolite.pm; we don't want to include that -# file here just for that little bit -{ - our $creator = $ENV{GL_CREATOR}; - our $readers = $ENV{GL_READERS}; - our $writers = $ENV{GL_WRITERS}; - our $gl_user = $ENV{GL_USER}; - die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; - - $repos{$ENV{GL_REPO}} = $repos{$ENV{GL_REPOPATT}} if ( $ENV{GL_REPOPATT} ); -} -my $reported_repo = $ENV{GL_REPO} . ( $ENV{GL_REPOPATT} ? " ($ENV{GL_REPOPATT})" : "" ); - -# we've started to need some common subs in what used to be a small, cute, -# little script that barely spanned a few lines :( require "$ENV{GL_BINDIR}/gitolite.pm"; +my ($perm, $creator, $wild) = &repo_rights($ENV{GL_REPO}); +my $reported_repo = $ENV{GL_REPO} . ( $ENV{GL_REPOPATT} ? " ($ENV{GL_REPOPATT})" : "" ); + # ---------------------------------------------------------------------------- # start... # ---------------------------------------------------------------------------- @@ -71,17 +56,17 @@ chomp($merge_base = `git merge-base $oldsha $newsha`) unless $oldsha eq '0' x 40 or $newsha eq '0' x 40; -# what are you trying to do? (is it 'W' or '+'?) -my $perm = 'W'; +# att_acc == attempted access -- what are you trying to do? (is it 'W' or '+'?) +my $att_acc = 'W'; # rewriting a tag is considered a rewind, in terms of permissions -$perm = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); +$att_acc = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40); # non-ff push to ref # notice that ref delete looks like a rewind, as it should -$perm = '+' if $oldsha ne $merge_base; +$att_acc = '+' if $oldsha ne $merge_base; # were any 'D' perms specified? If they were, it means we have to separate # deletes from rewinds, so if the new sha is all 0's, change the '+' to a 'D' -$perm = 'D' if ( $repos{$ENV{GL_REPO}}{DELETE_IS_D} or $repos{'@all'}{DELETE_IS_D} ) and $newsha eq '0' x 40; +$att_acc = 'D' if ( $repos{$ENV{GL_REPO}}{DELETE_IS_D} or $repos{'@all'}{DELETE_IS_D} ) and $newsha eq '0' x 40; my @allowed_refs; # @all repos: see comments in similar code in check_access @@ -113,12 +98,12 @@ if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) { # log is the *first* one (which is a *real* ref, like refs/heads/master), # while all the rest (if they exist) are like NAME/something. So we do the # first one separately to capture it, then run the rest (if any) -my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, (shift @refs), $perm); -&check_ref (\@allowed_refs, $ENV{GL_REPO}, $_ , $perm) for @refs; +my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, (shift @refs), $att_acc); +&check_ref (\@allowed_refs, $ENV{GL_REPO}, $_ , $att_acc) for @refs; # if we returned at all, all the checks succeeded, so we log the action and exit 0 -&log_it("$ENV{GL_TS} $perm\t" . +&log_it("$ENV{GL_TS} $att_acc\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . "\t$reported_repo\t$ref\t$ENV{GL_USER}\t$log_refex\n"); diff --git a/src/gitolite.pm b/src/gitolite.pm index 0d64f17..ff1ca5d 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -1,4 +1,7 @@ use strict; +use Data::Dumper; +$Data::Dumper::Deepcopy = 1; + # this file is commonly used using "require". It is not required to use "use" # (because it doesn't live in a different package) @@ -34,8 +37,9 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple patter our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file -our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED); +our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG); our %repos; +our %groups; # ---------------------------------------------------------------------------- # convenience subs @@ -253,24 +257,39 @@ sub parse_acl # want the config dumped as is, really return unless $repo; - # return with "no wildcard match" status if you found the actual repo in - # the config or if wild is unset - return $ENV{GL_REPOPATT} = "" if $repos{$repo} or not $GL_WILDREPOS; + my ($wild, @repo_plus, @user_plus); + # expand $repo and $gl_user into all possible matching values + ($wild, @repo_plus) = &get_memberships($repo, 1); + ( @user_plus) = &get_memberships($gl_user, 0); + # XXX testing notes: the above should return just one entry during + # non-BC usage, whether wild or not + die "assert 1 failed" if (@repo_plus > 1 and $repo_plus[-1] ne '@all' + or @repo_plus > 2) and not $GL_BIG_CONFIG; - # didn't find actual repo in %repos, and wild is set, so find the repo - # pattern that matches the actual repo - my @matched = grep { $repo =~ /^$_$/ } sort keys %repos; + # the old "convenience copy" thing. Now on steroids :) - # didn't find a match? avoid leaking info to user about repo existence; - # as before, pretend "no wildcard match" status - return $ENV{GL_REPOPATT} = "" unless @matched; + # note that when copying the @all entry, we retain the destination name as + # @all; we dont change it to $repo or $gl_user + for my $r ('@all', @repo_plus) { + my $dr = $repo; $dr = '@all' if $r eq '@all'; + $repos{$dr}{DELETE_IS_D} = 1 if $repos{$r}{DELETE_IS_D}; + $repos{$dr}{NAME_LIMITS} = 1 if $repos{$r}{NAME_LIMITS}; - die "$repo has multiple matches\n@matched\n" if @matched > 1; + for my $u ('@all', @user_plus) { + my $du = $gl_user; $du = '@all' if $u eq '@all'; + $repos{$dr}{C}{$du} = 1 if $repos{$r}{C}{$u}; + $repos{$dr}{R}{$du} = 1 if $repos{$r}{R}{$u}; + $repos{$dr}{W}{$du} = 1 if $repos{$r}{W}{$u}; - # found exactly one pattern that matched, copy its ACL for convenience - $repos{$repo} = $repos{$matched[0]}; - # and return the pattern - return $ENV{GL_REPOPATT} = $matched[0]; + next if $r eq $dr and $u eq $du; # no point duplicating those refexes + push @{ $repos{$dr}{$du} }, @{ $repos{$r}{$u} } + if exists $repos{$r}{$u} and ref($repos{$r}{$u}) eq 'ARRAY'; + } + } + + $ENV{GL_REPOPATT} = ""; + $ENV{GL_REPOPATT} = $wild if $wild and $GL_WILDREPOS; + return ($wild); } # ---------------------------------------------------------------------------- @@ -295,6 +314,11 @@ sub report_basic &report_version($GL_ADMINDIR, $user); print "\rthe gitolite config gives you the following access:\r\n"; for my $r (sort keys %repos) { + if ($r =~ $REPONAME_PATT) { + &parse_acl($GL_CONF_COMPILED, $r, "NOBODY", "NOBODY", "NOBODY"); + } else { + &parse_acl($GL_CONF_COMPILED, $r, $ENV{GL_USER}, "NOBODY", "NOBODY"); + } # @all repos; meaning of read/write flags: # @R => @all users are allowed access to this repo # #R => you're a super user and can see @all repos @@ -331,7 +355,7 @@ sub expand_wild # actual_repo has to match the pattern being expanded next unless $actual_repo =~ /$repo/; - my($perm, $creator) = &repo_rights($actual_repo); + my($perm, $creator, $wild) = &repo_rights($actual_repo); next unless $perm =~ /\S/; print "$perm\t$creator\t$actual_repo\n"; } @@ -342,64 +366,67 @@ sub expand_wild # how/why). Regardless of how we're called, we assume $ENV{GL_USER} is # already defined { - my %normal_repos; - + my $last_repo = ''; sub repo_rights { my $repo = shift; $repo =~ s/^\.\///; $repo =~ s/\.git$//; + return if $last_repo eq $repo; # a wee bit o' caching, though not yet needed + # we get passed an actual repo name. It may be a normal # (non-wildcard) repo, in which case it is assumed to exist. If it's # a wildrepo, it may or may not exist. If it doesn't exist, the "C" # perms are also filled in, else that column is left blank - unless (%normal_repos) { - unless ($REPO_BASE) { - # means we've been called from outside - &where_is_rc(); - die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; - } - - &parse_acl($GL_CONF_COMPILED, "", "NOBODY", "NOBODY", "NOBODY"); - %normal_repos = %repos; + unless ($REPO_BASE) { + # means we've been called from outside; see doc/admin-defined-commands.mkd + &where_is_rc(); + die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; } - my $creator; my $perm = ' '; + my $creator; - # if repo is present "as is" in the config, those permissions will - # override anything inherited from a wildcard that may have matched - if ($normal_repos{$repo}) { - %repos = %normal_repos; - $creator = ''; - } elsif ( -d "$ENV{GL_REPO_BASE_ABS}/$repo.git" ) { - # must be a wildrepo, and it has already been created; find the - # creator and subsitute in repos + # get basic info about the repo and fill %repos + my $wild = ''; + my $exists = -d "$ENV{GL_REPO_BASE_ABS}/$repo.git"; + if ($exists) { + # these will be empty if it's not a wildcard repo anyway my ($read, $write); ($creator, $read, $write) = &wild_repo_rights($ENV{GL_REPO_BASE_ABS}, $repo, $ENV{GL_USER}); # get access list with these substitutions - &parse_acl($GL_CONF_COMPILED, $repo, $creator || "NOBODY", $read || "NOBODY", $write || "NOBODY"); + $wild = &parse_acl($GL_CONF_COMPILED, $repo, $creator || "NOBODY", $read || "NOBODY", $write || "NOBODY"); + } else { + $wild = &parse_acl($GL_CONF_COMPILED, $repo, $ENV{GL_USER}, "NOBODY", "NOBODY"); + } + + if ($exists and not $wild) { + $creator = ''; + } elsif ($exists) { + # is a wildrepo, and it has already been created $creator = "($creator)"; } else { - # repo didn't exist; C perms also need to be filled in after - # getting access list with only creator filled in - &parse_acl($GL_CONF_COMPILED, $repo, $ENV{GL_USER}, "NOBODY", "NOBODY"); + # repo didn't exist; C perms need to be filled in $perm = ( $repos{$repo}{C}{'@all'} ? ' @C' : ( $repos{$repo}{C}{$ENV{GL_USER}} ? ' =C' : ' ' )) if $GL_WILDREPOS; # if you didn't have perms to create it, delete the "convenience" # copy of the ACL that parse_acl makes delete $repos{$repo} unless $perm =~ /C/; - $creator = ""; + $creator = ""; } $perm .= ( $repos{$repo}{R}{'@all'} ? ' @R' : ( $repos{'@all'}{R}{$ENV{GL_USER}} ? ' #R' : ( $repos{$repo}{R}{$ENV{GL_USER}} ? ' R' : ' ' ))); $perm .= ( $repos{$repo}{W}{'@all'} ? ' @W' : ( $repos{'@all'}{W}{$ENV{GL_USER}} ? ' #W' : ( $repos{$repo}{W}{$ENV{GL_USER}} ? ' W' : ' ' ))); - return($perm, $creator); + + # set up for caching %repos + $last_repo = $repo; + + return($perm, $creator, $wild); } } # helper/convenience routine to get rights and ownership from a shell command sub cli_repo_rights { - my ($perm, $creator) = &repo_rights($_[0]); + my ($perm, $creator, $wild) = &repo_rights($_[0]); $perm =~ s/ /_/g; $creator =~ s/^\(|\)$//g; print "$perm $creator\n"; @@ -443,6 +470,78 @@ sub special_cmd } } +# ---------------------------------------------------------------------------- +# get memberships +# ---------------------------------------------------------------------------- + +# given a plain reponame or username, return: +# - the name itself, plus all the groups it belongs to if $GL_BIG_CONFIG is +# set +# OR +# - (for repos) if the name itself doesn't exist in the config, a wildcard +# matching it, plus all the groups that wildcard belongs to (again if +# $GL_BIG_CONFIG is set) + +# A name can normally appear (repo example) (user example) +# - directly (repo foo) (RW = bar) +# - (only for repos) as a direct wildcard (repo foo/.*) +# but if $GL_BIG_CONFIG is set, it can also appear: +# - indirectly (@g = foo; repo @g) (@ug = bar; RW = @ug)) +# - (only for repos) as an indirect wildcard (@g = foo/.*; repo @g). +# things that may not be obvious from the above: +# - the wildcard stuff does not apply to username memberships +# - for repos, wildcard appearances are TOTALLY ignored if a non-wild +# appearance (direct or indirect) exists + +sub get_memberships { + my $base = shift; # reponame or username + my $is_repo = shift; # some true value means a repo name has been passed + + my $wild = ''; + my (@ret, @ret_w); # maintain wild matches separately from non-wild + + # direct + push @ret, $base if not $is_repo or exists $repos{$base}; + if ($is_repo and $GL_WILDREPOS and not @ret) { + for my $i (sort keys %repos) { + if ($base =~ /^$i$/) { + die "$ABRT $base matches $wild AND $i\n" if $wild and $wild ne $i; + $wild = $i; + # direct wildcard + push @ret_w, $i; + } + } + } + + if ($GL_BIG_CONFIG) { + for my $g (sort keys %groups) { + for my $i (sort keys %{ $groups{$g} }) { + if ($base eq $i) { + # indirect + push @ret, $g; + } elsif ($is_repo and $GL_WILDREPOS and not @ret and $base =~ /^$i$/) { + die "$ABRT $base matches $wild AND $i\n" if $wild and $wild ne $i; + $wild = $i; + # indirect wildcard + push @ret_w, $g; + } + } + } + } + + # deal with returning user info first + return (@ret) unless $is_repo; + + # enforce the rule about ignoring all wildcard matches if a non-wild match + # exists while returning. (The @ret gating above does not adequately + # ensure this, it is only an optimisation). + # + # Also note that there is an extra return value when called for repos + # (compared to usernames) + + return ((@ret ? '' : $wild), (@ret ? @ret : @ret_w)); +} + # ---------------------------------------------------------------------------- # generic check access routine # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index b888af0..7e7faff 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -28,6 +28,7 @@ our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMIND # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; +our %groups; # the common setup module is in the same directory as this running program is my $bindir = $0; @@ -55,6 +56,10 @@ my $repo_base_abs = $ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE # start... # ---------------------------------------------------------------------------- +# no arguments given? default user is $USER (fedorahosted works like this, +# and it is harmless for others) +@ARGV = ($ENV{USER}) unless @ARGV; + # if the first argument is a "-s", this user is allowed to get a shell using # this key my $shell_allowed = 0; @@ -184,7 +189,7 @@ $ENV{GL_REPO}=$repo; # first level permissions check # ---------------------------------------------------------------------------- -my ($perm, $creator) = &repo_rights($repo); +my ($perm, $creator, $wild) = &repo_rights($repo); if ($perm =~ /C/) { # it was missing, and you have create perms wrap_chdir("$repo_base_abs"); diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 220d4b8..0f761d0 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_SETPERMS_OVERRIDES_CONFIG); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_SETPERMS_OVERRIDES_CONFIG, $GL_BIG_CONFIG); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -181,10 +181,12 @@ sub parse_conf_file # repo(s) elsif (/^repo (.*)/) { - # grab the list and expand any @stuff in it + # grab the list... @repos = split ' ', $1; unless (@repos == 1 and $repos[0] eq '@all') { - @repos = expand_list ( @repos ); + # ...expand groups in the default case + @repos = expand_list ( @repos ) unless $GL_BIG_CONFIG; + # ...sanity check for (@repos) { die "$ABRT bad reponame $_\n" if ($GL_WILDREPOS and $_ !~ $REPOPATT_PATT); @@ -214,7 +216,7 @@ sub parse_conf_file # expand the user list, unless it is just "@all" @users = expand_list ( @users ) - unless (@users == 1 and $users[0] eq '@all'); + unless ($GL_BIG_CONFIG or (@users == 1 and $users[0] eq '@all')); do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; s/\bCREAT[EO]R\b/~\$creator/g for @users; @@ -370,6 +372,7 @@ my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); # much... $dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~*(.*?)'/"$1"/g; print $compiled_fh $dumped_data; +print $compiled_fh Data::Dumper->Dump([\%groups], [qw(*groups)]) if $GL_BIG_CONFIG and %groups; close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; # ---------------------------------------------------------------------------- @@ -406,8 +409,7 @@ wrap_chdir("$repo_base_abs"); for my $repo (sort keys %repos) { next unless $repo =~ $REPONAME_PATT; - next if $repo =~ m(^EXTCMD/); # these are not real repos - next if $repo eq '@all'; + next if $repo =~ m(^\@|EXTCMD/); # these are not real repos unless (-d "$repo.git") { print STDERR "creating $repo...\n"; new_repo($repo, "$GL_ADMINDIR/hooks/common"); @@ -457,6 +459,7 @@ wrap_chdir("$repo_base_abs"); # daemons first... for my $repo (sort keys %repos) { next unless $repo =~ $REPONAME_PATT; + next if $repo =~ m(^\@|EXTCMD/); # these are not real repos my $export_ok = "$repo.git/git-daemon-export-ok"; if ($repos{$repo}{'R'}{'daemon'}) { system("touch $export_ok"); @@ -469,6 +472,7 @@ my %projlist = (); # ...then gitwebs for my $repo (sort keys %repos) { next unless $repo =~ $REPONAME_PATT; + next if $repo =~ m(^\@|EXTCMD/); # these are not real repos my $desc_file = "$repo.git/description"; # note: having a description also counts as enabling gitweb if ($repos{$repo}{'R'}{'gitweb'} or $desc{"$repo.git"}) { From 346b39684032affdbc72532d16f82592db084573 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 14 May 2010 20:21:04 +0530 Subject: [PATCH 356/850] (minor) move version check to the right place --- src/gl-compile-conf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index a6df657..220d4b8 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -392,6 +392,12 @@ my ($gv_maj, $gv_min, $gv_patchrel) = ($git_version =~ m/git version (\d+)\.(\d+ die "$ABRT I can't understand $git_version\n" unless ($gv_maj >= 1); $git_version = $gv_maj*10000 + $gv_min*100 + $gv_patchrel; # now it's "normalised" +die "\n\t\t***** AAARGH! *****\n" . + "\tyour git version is older than 1.6.2\n" . + "\tsince that is now more than one year old, and gitolite needs some of\n" . + "\tthe newer features, please upgrade.\n" + if $git_version < 10602; # that's 1.6.2 to you + # repo-base needs to be an absolute path for this loop to work right # so if it was not already absolute, prefix $HOME. my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); @@ -421,12 +427,6 @@ for my $repo (sort keys %repos) { } } -die "\n\t\t***** AAARGH! *****\n" . - "\tyour git version is older than 1.6.2\n" . - "\tsince that is now more than one year old, and gitolite needs some of\n" . - "\tthe newer features, please upgrade.\n" - if $git_version < 10602; # that's 1.6.2 to you - # ---------------------------------------------------------------------------- # update repo configurations # ---------------------------------------------------------------------------- From c8f83a03dd422370882abbc2ef0f9e315af74c96 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 14 May 2010 20:40:59 +0530 Subject: [PATCH 357/850] (big-config) compile: create new repos even if GL_BIG_CONFIG is set ...by expanding the groups of course --- src/gl-compile-conf | 48 +++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 0f761d0..dd96381 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -405,27 +405,37 @@ die "\n\t\t***** AAARGH! *****\n" . # so if it was not already absolute, prefix $HOME. my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); -wrap_chdir("$repo_base_abs"); +{ + wrap_chdir("$repo_base_abs"); -for my $repo (sort keys %repos) { - next unless $repo =~ $REPONAME_PATT; - next if $repo =~ m(^\@|EXTCMD/); # these are not real repos - unless (-d "$repo.git") { - print STDERR "creating $repo...\n"; - new_repo($repo, "$GL_ADMINDIR/hooks/common"); - # new_repo would have chdir'd us away; come back - wrap_chdir("$repo_base_abs"); - } + # autocreate repos. Start with the ones that are normal repos in %repos + my @repos = grep { $_ =~ $REPONAME_PATT and not /^@/ } sort keys %repos; + # then, for each repogroup, find the members of the group and add them in + map { push @repos, keys %{ $groups{$_} } } grep { /^@/ } keys %repos; + # weed out duplicates (the code in the loop below is disk activity!) + my %seen = map { $_ => 1 } @repos; + @repos = sort keys %seen; - # when repos are copied over from elsewhere, one had to run easy install - # once again to make the new (OS-copied) repo contain the proper update - # hook. Perhaps we can make this easier now, and eliminate the easy - # install, with a quick check (and a new, empty, "hook" as a sentinel) - unless (-l "$repo.git/hooks/gitolite-hooked") { - ln_sf("$GL_ADMINDIR/hooks/common", "*", "$repo.git/hooks"); - # in case of package install, GL_ADMINDIR is no longer the top cop; - # override with the package hooks - ln_sf("$GL_PACKAGE_HOOKS/common", "*", "$repo.git/hooks") if $GL_PACKAGE_HOOKS; + for my $repo (sort @repos) { + next unless $repo =~ $REPONAME_PATT; + next if $repo =~ m(^\@|EXTCMD/); # these are not real repos + unless (-d "$repo.git") { + print STDERR "creating $repo...\n"; + new_repo($repo, "$GL_ADMINDIR/hooks/common"); + # new_repo would have chdir'd us away; come back + wrap_chdir("$repo_base_abs"); + } + + # when repos are copied over from elsewhere, one had to run easy install + # once again to make the new (OS-copied) repo contain the proper update + # hook. Perhaps we can make this easier now, and eliminate the easy + # install, with a quick check (and a new, empty, "hook" as a sentinel) + unless (-l "$repo.git/hooks/gitolite-hooked") { + ln_sf("$GL_ADMINDIR/hooks/common", "*", "$repo.git/hooks"); + # in case of package install, GL_ADMINDIR is no longer the top cop; + # override with the package hooks + ln_sf("$GL_PACKAGE_HOOKS/common", "*", "$repo.git/hooks") if $GL_PACKAGE_HOOKS; + } } } From 0139fe0e9716d7035ce81a19a0987e4119234b43 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 14 May 2010 18:20:31 +0530 Subject: [PATCH 358/850] (big-config) compile: dont complain about "@foo" not having a pubkey --- src/gl-compile-conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index dd96381..9dbb6fb 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -576,7 +576,7 @@ for my $pubkey (`find . -type f`) # lint check 3; a little more severe than the first two I guess... for my $user (sort keys %user_list) { - next if $user =~ /^(gitweb|daemon|\@all|~\$creator|\$readers|\$writers)$/ or $user_list{$user} eq 'has pubkey'; + next if $user =~ /^(gitweb|daemon|\@.*|~\$creator|\$readers|\$writers)$/ or $user_list{$user} eq 'has pubkey'; print STDERR "$WARN user $user in config, but has no pubkey!\n"; } From 42c3543e9707cbce2303e5216240927426034ff1 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Fri, 14 May 2010 19:00:40 +0300 Subject: [PATCH 359/850] contrib: Add gitweb example configurations Move the example code from doc/3 to contrib/gitweb/ and modify it to work with both wildcard and non-wildcard setups. Signed-off-by: Teemu Matilainen --- contrib/gitweb/gitweb.conf | 40 ++++++++++++++++++++++++++++++++ doc/3-faq-tips-etc.mkd | 47 ++++---------------------------------- 2 files changed, 44 insertions(+), 43 deletions(-) create mode 100644 contrib/gitweb/gitweb.conf diff --git a/contrib/gitweb/gitweb.conf b/contrib/gitweb/gitweb.conf new file mode 100644 index 0000000..efe93a3 --- /dev/null +++ b/contrib/gitweb/gitweb.conf @@ -0,0 +1,40 @@ +# -------------------------------------------- +# Per-repo authorization based on gitolite ACL +# Include this in gitweb.conf +# See doc/3-faq-tips-etc.mkd for more info + +# HOME of the gitolite user +my $gl_home = "/home/git"; + +# environment variables needed by gitolite.pm +$ENV{GL_RC} = "$gl_home/.gitolite.rc"; +$ENV{GL_USER} = $cgi->remote_user; + +# variables from the RC file +our ($REPO_BASE, $GL_ADMINDIR); + +# set HOME temporarily for RC parsing +my $orig_home = $ENV{HOME}; +$ENV{HOME} = $gl_home; +do $ENV{GL_RC} + or die_error(500, "Failed to parse $ENV{GL_RC}: " . ($! or $@)); +$ENV{HOME} = $orig_home; + +# set project root etc. absolute paths +$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$gl_home/$REPO_BASE" ); +$projects_list = $projectroot = $ENV{GL_REPO_BASE_ABS}; + +# load gitolite helper routines +require "$GL_ADMINDIR/src/gitolite.pm" + or die_error(500, "Failed to parse gitolite.pm: " . ($! or $@)); + +$export_auth_hook = sub { + my $repo = shift; + # gitweb passes us the full repo path; so we strip the beginning + # and the end, to get the repo name as it is specified in gitolite conf + return unless $repo =~ s/^\Q$projectroot\E\/?(.+)\.git$/$1/; + + # check for (at least) "R" permission + my ($perm, $creator) = &repo_rights($repo); + return ($perm =~ /R/); +}; diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index ffb5f43..06f6799 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -588,51 +588,12 @@ the users *manage their own HTTP passwords*. Here's how: * tell your users to type in `ssh git@server htpasswd` to set or change their HTTP passwords -Here's the rest of how it hangs together. +Of course some other authentication method can be used (e.g. `mod_ldap`) as +long as the usernames match. Gitweb allows you to specify a subroutine to decide on access. We use that -feature and tie it to gitolite. Sample code (untested by me, but others do -use it, munged from something I saw [here][leho]) is given below. - -Note the **utter simplicity** of the actual check (just 1 line!). This is an -unexpected piece of luck coming from the decision to keep the config parse -separate from the actual access control. The config parser puts a pure perl -hash in that file named below as `$gl_conf_compiled`, so all the parsing is -already done and we just use it! - - # completely untested... but the basic idea should work fine - - # change these as needed - # projectroot should be the same as gitolite's REPO_BASE, but converted to - # an absolute path - $projectroot = '/home/git/repositories/'; - my $gl_conf_compiled = '/home/git/.gitolite/conf/gitolite.conf-compiled.pm'; - - # I am told this gives us the HTTP auth username - my $username = $cgi->remote_user; - - # ---------- - - # parse the config file; updates %repos hash - our %repos; - die "parse $gl_conf_compiled failed: " . ($! or $@) unless do $gl_conf_compiled; - - # this is gitweb's mechanism; it calls whatever sub is pointed at by this - # variable to decide access yes/no. Gitweb calls it with one argument - # containing the full path of the repo being accessed - $export_auth_hook = sub { - my $reponame = shift; - # take the full path provided, strip the beginning... - $reponame =~ s/\Q$projectroot\E\/?//; - # ...and the end, to get the repo name as it is specified in gitolite conf - $reponame =~ s/\.git$//; - - return exists $repos{$reponame}{R}{$username} - || exists $repos{$reponame}{R}{'@all'}; - }; - - -[leho]: http://leho.kraav.com/news/2009/10/27/using-apache-authentication-with-gitweb-gitosis-repository-access-control/ +feature and tie it to gitolite. Configuration example can be found in +`contrib/gitweb/`. ### advanced features From d11a27924b83c8b795d445eb2ace9fddc3041a42 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 12 May 2010 22:09:51 +0530 Subject: [PATCH 360/850] (big-config) compile: fragments in big-config Since it is possible to do all sorts of shenanigans with wildcards and repo groups, we - allow only a fragment called "foo" to set permissions for a group called "@foo", in addition to a repo called "foo" - forbid defining any groups within a fragment conf. All "@foo = bar baz" must be done in the main config file now. If this proves too limiting for anyone I'll worry about it then. --- src/gl-compile-conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 9dbb6fb..5c02160 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -173,6 +173,8 @@ sub parse_conf_file # user or repo groups if (/^(@\S+) = (.*)/) { + die "$ABRT defining groups is not allowed inside fragments\n" + if $GL_BIG_CONFIG and $fragment ne 'master'; # store the members of each group as hash key. Keep track of when # the group was *first* created by using $fragment as the *value* do { $groups{$1}{$_} ||= $fragment } for ( expand_list( split(' ', $2) ) ); @@ -239,6 +241,8 @@ sub parse_conf_file # fragment is also called 'foo' (you're allowed to have a # fragment that is only concerned with one repo) ( $fragment eq $repo ) or + # same thing in big-config-land; foo is just @foo now + ( $GL_BIG_CONFIG and "\@$fragment" eq $repo ) or # fragment is called "bar" and "@bar = foo" has been # defined in the master config ( ($groups{"\@$fragment"}{$repo} || '') eq 'master' ) From 8da223f92a130256a7640d89b1508c2b1ef55ab4 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 14 May 2010 16:35:37 +0530 Subject: [PATCH 361/850] (big-config) allow usergroup information to be passed in from outside [Please NOTE: this is all about *user* groups, not *repo* groups] SUMMARY: gl-auth-commmand can now take an optional list of usergroup names after the first argument (which is the username). See doc/big-config.mkd in the next commit or so --- src/gitolite.pm | 8 +++++++- src/gl-auth-command | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index ff1ca5d..9501257 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -530,7 +530,13 @@ sub get_memberships { } # deal with returning user info first - return (@ret) unless $is_repo; + unless ($is_repo) { + # add in group membership info sent in via second and subsequent + # arguments to gl-auth-command; be sure to prefix the "@" sign to each + # of them! + push @ret, map { s/^/@/; $_; } split(' ', $ENV{GL_GROUP_LIST}) if $ENV{GL_GROUP_LIST}; + return (@ret); + } # enforce the rule about ignoring all wildcard matches if a non-wild match # exists while returning. (The @ret gating above does not adequately diff --git a/src/gl-auth-command b/src/gl-auth-command index 7e7faff..80fd393 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -71,6 +71,10 @@ if ($ARGV[0] eq '-s') { # first, fix the biggest gripe I have with gitosis, a 1-line change my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! +# if there are any more arguments, they're a list of group names that the user +# is a member of +$ENV{GL_GROUP_LIST} = join(" ", @ARGV) if @ARGV; + # ---------------------------------------------------------------------------- # logging, timestamp env vars # ---------------------------------------------------------------------------- From 58fc6a32529e639d60a70a96fbc32f6201e20ca6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 14 May 2010 18:20:57 +0530 Subject: [PATCH 362/850] (big-config) documentation --- doc/big-config.mkd | 200 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 doc/big-config.mkd diff --git a/doc/big-config.mkd b/doc/big-config.mkd new file mode 100644 index 0000000..a1fe0ad --- /dev/null +++ b/doc/big-config.mkd @@ -0,0 +1,200 @@ +# what is a "big-config" + +In this document: + + * when/why do we need it? + * how do we use it? + * what are the downsides? + * (extra coolness) usergroups and LDAP/similar tools + +### when/why do we need it? + +A "big config" is anything that has more than a few thousand users and a few +thousand repos, organised into groups that are much smaller in number (like +maybe a few hundreds of repogroups and a few dozens of usergroups). + +So let's say you have + + @wbr = lynx firefox + @devs = alice bob + + repo @wbr + RW+ next = @devs + RW master = @devs + +Gitolite internally translates this to + + repo lynx firefox + RW+ next = alice bob + RW master = alice bob + +Not just that -- it now generates the actual config rules once for each +user-repo-ref combination (there are 8 combinations above; the compiled config +file looks partly like this: + + %repos = ( + 'firefox' => { + 'R' => { + 'alice' => 1, + 'bob' => 1 + }, + 'W' => { + 'alice' => 1, + 'bob' => 1 + }, + 'alice' => [ + { + 'refs/heads/next' => 'RW+' + }, + { + 'refs/heads/master' => 'RW' + } + ], + 'bob' => [ + { + 'refs/heads/next' => 'RW+' + }, + { + 'refs/heads/master' => 'RW' + } + ] + }, + 'lynx' => { + 'R' => { + 'alice' => 1, + 'bob' => 1 + }, + 'W' => { + 'alice' => 1, + 'bob' => 1 + }, + 'alice' => [ + { + 'refs/heads/next' => 'RW+' + }, + { + 'refs/heads/master' => 'RW' + } + ], + 'bob' => [ + { + 'refs/heads/next' => 'RW+' + }, + { + 'refs/heads/master' => 'RW' + } + ] + } + ); + +Phew! + +You can imagine what that does when you have 10,000 users and 10,000 repos. +Let's just say it's not pretty :) + +### how do we use it? + +Now, if you had all those 10,000 users and repos explicitly listed (no +groups), then there is no help. But if, like the above example, you had +groups like we used above, there is hope. + +Just set + + $GL_BIG_CONFIG = 1; + +in the `~/.gitolite.rc` file on the server. When you do that, and push this +configuration, the compiled file looks like this: + + %repos = ( + '@wbr' => { + '@devs' => [ + { + 'refs/heads/next' => 'RW+' + }, + { + 'refs/heads/master' => 'RW' + } + ], + 'R' => { + '@devs' => 1 + }, + 'W' => { + '@devs' => 1 + } + }, + ); + %groups = ( + '@devs' => { + 'alice' => 'master', + 'bob' => 'master' + }, + '@wbr' => { + 'firefox' => 'master', + 'lynx' => 'master' + } + ); + +That's a lot smaller, and allows orders of magintude more repos and groups to +be supported. + +### what are the downsides? + +There are a few minor issues. + +First, "deny" rules (rules whose "permission" is a "-" sign), will not work +exactly the same as before. + +[TODO: add a nice example etc...] + +Second, if you use the delegation feature, you can no longer define or extend +@groups in a fragment, for security reasons. It will also not let you use any +group other than the @fragname itself (specifically, groups which contained a +subset of the allowed @fragname, which would work normally, do not work now). + +(If you didn't understand all that, you're probably not using delegation, so +feel free to ignore it!) + +### (extra coolness) usergroups and LDAP/similar tools + +[Please NOTE: this is all about *user* groups, not *repo* groups] + +Gitolite now allows usergroup information to be passed in from outside. The +`gl-auth-commmand` can now take an optional list of usergroup names after the +first argument (which is the username). + +To understand why this is useful, consider the following: + +Some people have an LDAP-backed ssh daemon (or some other similar mechanism +that can speak "ssh" to the client), with pubkeys stored in LDAP, etc., and +some way (not using `~/.ssh/authorized_keys`) of invoking gitolite. + +Such setups also have "usergroups", and a way to tell, for each user, what +groups he/she is a member of. So let's say "alice" works on projects "foo" +and "bar", while "bob" is works on project "bar" and is a member of the +`L3_support` team. + +You can use those group names in the gitolite config file for access control +as "@foo", "@bar", `@L3_support`, etc.; please note the "@" prefix because +gitolite requires it. + +However, that still leaves a wee little inconvenience. You still have to add +lines like this to the gitolite config file: + + @foo = alice + @bar = alice bob + @L3_support = bob + +You don't need to do that anymore now. Tell your authentication system that, +after authenticating alice, instead of running: + + /some/path/to/gl-auth-command alice + +it should first find the groups that alice is a member of, and then run: + + /some/path/to/gl-auth-command alice foo bar + +That's it. Internally, gitolite will behave as if the config file had also +specified: + + @foo = alice + @bar = alice From b278d430b8fee76f247e66fc621696254db367af Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Fri, 14 May 2010 19:23:09 +0300 Subject: [PATCH 363/850] contrib: Add information about gitolite-tools Signed-off-by: Teemu Matilainen --- contrib/gitolite-tools.mkd | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 contrib/gitolite-tools.mkd diff --git a/contrib/gitolite-tools.mkd b/contrib/gitolite-tools.mkd new file mode 100644 index 0000000..887ff98 --- /dev/null +++ b/contrib/gitolite-tools.mkd @@ -0,0 +1,15 @@ +# gitolite-tools + +gitolite-tools is a collection of external git commands to work with +gitolite server and repositories: + +* git gl-info - Display gitolite server information +* git gl-ls - List accessible gitolite repositories +* git gl-desc - Display or edit description of gitolite wildcard repositories +* git gl-perms - Display or edit permissions of gitolite wildcard repositories +* git gl-htpasswd - Set password for gitweb/apache + +## Homepage + +The project in GitHub: +[http://github.com/tmatilai/gitolite-tools](http://github.com/tmatilai/gitolite-tools) From 35750c1abec18e38d1452c756b8c5e59d861308f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 16 May 2010 06:18:08 +0530 Subject: [PATCH 364/850] (big-config) update doc and rc, allow skipping gitweb/daemon skipping gitweb/daemon has an enormous impact on speed of an admin-push! --- conf/example.gitolite.rc | 7 +++- doc/big-config.mkd | 25 +++++++++-- src/gl-compile-conf | 90 ++++++++++++++++++++-------------------- 3 files changed, 74 insertions(+), 48 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index ecaaaa5..4821596 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -1,5 +1,4 @@ # paths and configuration variables for gitolite -$GL_BIG_CONFIG = 1; # please read comments before editing @@ -83,7 +82,13 @@ $GIT_PATH=""; # -------------------------------------- +# ---------------------------------------------------------------------- +# BIG CONFIG SETTINGS +# Please read doc/big-config.mkd for details + +$GL_BIG_CONFIG = 0; +$GL_NO_DAEMON_NO_GITWEB = 0; # ---------------------------------------------------------------------- # SECURITY SENSITIVE SETTINGS diff --git a/doc/big-config.mkd b/doc/big-config.mkd index a1fe0ad..5ff551f 100644 --- a/doc/big-config.mkd +++ b/doc/big-config.mkd @@ -4,14 +4,15 @@ In this document: * when/why do we need it? * how do we use it? + * summary of settings in RC file * what are the downsides? * (extra coolness) usergroups and LDAP/similar tools ### when/why do we need it? -A "big config" is anything that has more than a few thousand users and a few -thousand repos, organised into groups that are much smaller in number (like -maybe a few hundreds of repogroups and a few dozens of usergroups). +A "big config" is anything that has a few thousand users and a few thousand +repos, organised into groups that are much smaller in number (like maybe a few +hundreds of repogroups and a few dozens of usergroups). So let's say you have @@ -137,6 +138,24 @@ configuration, the compiled file looks like this: That's a lot smaller, and allows orders of magintude more repos and groups to be supported. +### summary of settings in RC file + +The default RC file contains the following lines: + + $GL_BIG_CONFIG = 0; + $GL_NO_DAEMON_NO_GITWEB = 0; + +The first setting means that by default, big-config is off; you can change it +to 1 to enable it. + +The second is a very useful optimisation that you *must* enable if you *do* +have a large number of repositories, and do *not* use gitolite's support for +gitweb or git-daemon access (see "[easier to specify gitweb description and +gitweb/daemon access][gw]" for details). This will save a lot of time when +you push the gitolite-admin repo with changes. + +[gw]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gitweb + ### what are the downsides? There are a few minor issues. diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 5c02160..e6d1268 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_SETPERMS_OVERRIDES_CONFIG, $GL_BIG_CONFIG); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_SETPERMS_OVERRIDES_CONFIG, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -470,58 +470,60 @@ for my $repo (keys %repo_config) { wrap_chdir("$repo_base_abs"); -# daemons first... -for my $repo (sort keys %repos) { - next unless $repo =~ $REPONAME_PATT; - next if $repo =~ m(^\@|EXTCMD/); # these are not real repos - my $export_ok = "$repo.git/git-daemon-export-ok"; - if ($repos{$repo}{'R'}{'daemon'}) { - system("touch $export_ok"); - } else { - unlink($export_ok); - } -} - -my %projlist = (); -# ...then gitwebs -for my $repo (sort keys %repos) { - next unless $repo =~ $REPONAME_PATT; - next if $repo =~ m(^\@|EXTCMD/); # these are not real repos - my $desc_file = "$repo.git/description"; - # note: having a description also counts as enabling gitweb - if ($repos{$repo}{'R'}{'gitweb'} or $desc{"$repo.git"}) { - $projlist{"$repo.git"} = 1; - # add the description file; no messages to user or error checking :) - $desc{"$repo.git"} and open(DESC, ">", $desc_file) and print DESC $desc{"$repo.git"} . "\n" and close DESC; - if ($owner{"$repo.git"}) { - # set the repository owner - system("git", "--git-dir=$repo.git", "config", "gitweb.owner", $owner{"$repo.git"}); +unless ($GL_NO_DAEMON_NO_GITWEB) { + # daemons first... + for my $repo (sort keys %repos) { + next unless $repo =~ $REPONAME_PATT; + next if $repo =~ m(^\@|EXTCMD/); # these are not real repos + my $export_ok = "$repo.git/git-daemon-export-ok"; + if ($repos{$repo}{'R'}{'daemon'}) { + system("touch $export_ok"); } else { + unlink($export_ok); + } + } + + my %projlist = (); + # ...then gitwebs + for my $repo (sort keys %repos) { + next unless $repo =~ $REPONAME_PATT; + next if $repo =~ m(^\@|EXTCMD/); # these are not real repos + my $desc_file = "$repo.git/description"; + # note: having a description also counts as enabling gitweb + if ($repos{$repo}{'R'}{'gitweb'} or $desc{"$repo.git"}) { + $projlist{"$repo.git"} = 1; + # add the description file; no messages to user or error checking :) + $desc{"$repo.git"} and open(DESC, ">", $desc_file) and print DESC $desc{"$repo.git"} . "\n" and close DESC; + if ($owner{"$repo.git"}) { + # set the repository owner + system("git", "--git-dir=$repo.git", "config", "gitweb.owner", $owner{"$repo.git"}); + } else { + # remove the repository owner setting + system("git --git-dir=$repo.git config --unset-all gitweb.owner 2>/dev/null"); + } + } else { + # delete the description file; no messages to user or error checking :) + unlink $desc_file; # remove the repository owner setting system("git --git-dir=$repo.git config --unset-all gitweb.owner 2>/dev/null"); } - } else { - # delete the description file; no messages to user or error checking :) - unlink $desc_file; - # remove the repository owner setting - system("git --git-dir=$repo.git config --unset-all gitweb.owner 2>/dev/null"); + + # unless there are other gitweb.* keys set, remove the section to keep the + # config file clean + my $keys = `git --git-dir=$repo.git config --get-regexp '^gitweb\\.' 2>/dev/null`; + if (length($keys) == 0) { + system("git --git-dir=$repo.git config --remove-section gitweb 2>/dev/null"); + } } - # unless there are other gitweb.* keys set, remove the section to keep the - # config file clean - my $keys = `git --git-dir=$repo.git config --get-regexp '^gitweb\\.' 2>/dev/null`; - if (length($keys) == 0) { - system("git --git-dir=$repo.git config --remove-section gitweb 2>/dev/null"); + # update the project list + my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); + for my $proj (sort keys %projlist) { + print $projlist_fh "$proj\n"; } + close $projlist_fh; } -# update the project list -my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); -for my $proj (sort keys %projlist) { - print $projlist_fh "$proj\n"; -} -close $projlist_fh; - # ---------------------------------------------------------------------------- # "compile" ssh authorized_keys # ---------------------------------------------------------------------------- From aa8da9301674874fe7f958c0d49d773e8333b5e3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 16 May 2010 13:13:25 +0530 Subject: [PATCH 365/850] tone down the "ZOMG users without pubkeys" hysteria :) --- src/gl-compile-conf | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index e6d1268..d6d6dbf 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -580,10 +580,16 @@ for my $pubkey (`find . -type f`) print $newkeys_fh $pubkey_content; } # lint check 3; a little more severe than the first two I guess... -for my $user (sort keys %user_list) { - next if $user =~ /^(gitweb|daemon|\@.*|~\$creator|\$readers|\$writers)$/ or $user_list{$user} eq 'has pubkey'; - print STDERR "$WARN user $user in config, but has no pubkey!\n"; + my @no_pubkey = + grep { $_ !~ /^(gitweb|daemon|\@.*|~\$creator|\$readers|\$writers)$/ } + grep { $user_list{$_} ne 'has pubkey' } + keys %user_list; + if (@no_pubkey > 10) { + print STDERR "$WARN You have " . scalar(@no_pubkey) . " users WITHOUT pubkeys...!\n"; + } elsif (@no_pubkey) { + print STDERR "$WARN the following users have no pubkeys:\n", join(",", sort @no_pubkey), "\n"; + } } print $newkeys_fh "# gitolite end\n"; From 32056e0b7f88c0df73f7f27b605d61bd51d4a05b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 18 May 2010 14:57:14 +0530 Subject: [PATCH 366/850] (big one!) rule sequencing changes! There were 2 problems with rule sequencing. Eli had a use case where everyone is equal, but some are more equal than the others ;-) He wanted a way to say "everyone can create repos under their own names, but only some people should be able to rewind their branches". Something like this would be ideal (follow the rules in sequence for u1/u2/u3/u4, and you will see that the "deny" rule kicks in to prevent u1/u2 from being able to rewind, although they can certainly delete their branches): @private-owners = u1 u2 @experienced-private-owners = u3 u4 repo CREATOR/.* C = @private-owners @experienced-private-owners RWD = CREATOR RW = WRITERS R = READERS - = @private-owners RW+D = CREATOR In normal gitolite this doesn't work because the CREATOR rules (which get translated to "u1" at runtime) end up over-writing the "deny" rule when u1 or u2 are the creators. This over-writing happens directly at the "do compiled.pm" step. With big-config, this does not happen (because @private-owners does not get expanded to u1 and u2), but the problem remains: the order of picking up elements of repo_plus and user_plus is such that, again, the RW+D wins (it appears before the "-" rule). We fix all that by - making CREATOR complete to more than just the creator's name (for "u1", it now becomes "u1 - wild", which is actually illegal to use for real so there's no possibility of a name clash!) - maintaining a rule sequence number that is used to sort the rules eventually applied (this also resulted in the refex+perm hash becoming a list) --- src/gitolite.pm | 11 ++++++----- src/gl-compile-conf | 8 ++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 9501257..fcc122f 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -75,14 +75,15 @@ sub check_ref { # codes etc., but for now we're happy to just die. my ($allowed_refs, $repo, $ref, $perm) = @_; - for my $ar (@{$allowed_refs}) { - my $refex = (keys %$ar)[0]; + my @allowed_refs = sort { $a->[0] <=> $b->[0] } @{$allowed_refs}; + for my $ar (@allowed_refs) { + my $refex = $ar->[1]; # refex? sure -- a regex to match a ref against :) next unless $ref =~ /^$refex/; - die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; + die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->[2] eq '-'; # as far as *this* ref is concerned we're ok - return $refex if ($ar->{$refex} =~ /\Q$perm/); + return $refex if ($ar->[2] =~ /\Q$perm/); } die "$perm $ref $repo $ENV{GL_USER} DENIED by fallthru\n"; } @@ -275,7 +276,7 @@ sub parse_acl $repos{$dr}{DELETE_IS_D} = 1 if $repos{$r}{DELETE_IS_D}; $repos{$dr}{NAME_LIMITS} = 1 if $repos{$r}{NAME_LIMITS}; - for my $u ('@all', @user_plus) { + for my $u ('@all', "$gl_user - wild", @user_plus) { my $du = $gl_user; $du = '@all' if $u eq '@all'; $repos{$dr}{C}{$du} = 1 if $repos{$r}{C}{$u}; $repos{$dr}{R}{$du} = 1 if $repos{$r}{R}{$u}; diff --git a/src/gl-compile-conf b/src/gl-compile-conf index d6d6dbf..a6064ee 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -95,6 +95,9 @@ our %groups = (); # in between :) my %repos = (); +# rule sequence number +my $rule_seq = 0; + # ... having been forced to use a list as described above, we lose some # efficiency due to the possibility of the same {ref, perms} pair showing up # multiple times for the same repo+user. So... @@ -286,8 +289,9 @@ sub parse_conf_file # that do *not* use NAME limits. Setting a flag that # can be checked right away will help us do that $repos{$repo}{NAME_LIMITS} = 1 if $ref =~ /^NAME\//; - push @{ $repos{$repo}{$user} }, { $ref => $perms } - unless $rurp_seen{$repo}{$user}{$ref}{$perms}++; + my $p_user = $user; $p_user =~ s/(creator|readers|writers)$/$1 - wild/; + push @{ $repos{$repo}{$p_user} }, [ $rule_seq++, $ref, $perms ] + unless $rurp_seen{$repo}{$p_user}{$ref}{$perms}++; } } } From be3d00079a1d5c4307be7e4a1674b3debafa698b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 18 May 2010 16:15:23 +0530 Subject: [PATCH 367/850] Revert "allow setperms to override config file permissions" This reverts commit 9612e3a4cc226ba78ca22ae733f1bd94a098ca28, since it is no longer needed as of the rule sequencing changes we just made. Conflicts: src/gl-compile-conf --- conf/example.gitolite.rc | 15 --------------- src/gl-compile-conf | 6 ++---- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 4821596..d7b18d8 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -119,7 +119,6 @@ $GL_NO_DAEMON_NO_GITWEB = 0; $GL_GITCONFIG_KEYS = ""; -# # The second choice is to give it a space separated list of settings you # consider safe. (These are actually treated as a set of regular expression # patterns, and any one of them must match). For example: @@ -190,20 +189,6 @@ $SVNSERVE = ""; $GL_WILDREPOS = 0; -# -------------------------------------- -# ALLOW SETPERMS TO OVERRIDE gitolite.conf - -# A user can be given permissions to use a "wildcard created" repo using the -# "setperms" command run by the creator. However, if that same user is also -# explicitly listed in the config file as having a specific permission, and if -# that permission is different from what was specified using `setperms`, there -# is a conflict. - -# Default behaviour is to let the config file permissions override the -# "setperms" permissions, but if you want it the other way, set this to 1. - -$GL_SETPERMS_OVERRIDES_CONFIG = 0; - # -------------------------------------- # HOOK CHAINING diff --git a/src/gl-compile-conf b/src/gl-compile-conf index a6064ee..63a677c 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_SETPERMS_OVERRIDES_CONFIG, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -227,8 +227,6 @@ sub parse_conf_file s/\bCREAT[EO]R\b/~\$creator/g for @users; s/\bREADERS\b/\$readers/g for @users; s/\bWRITERS\b/\$writers/g for @users; - # and double it up if $GL_SETPERMS_OVERRIDES_CONFIG - do { s/\$(creator|readers|writers)\b/~\$$1/g for @users } if $GL_SETPERMS_OVERRIDES_CONFIG; # ok, we can finally populate the %repos hash for my $repo (@repos) # each repo in the current stanza @@ -378,7 +376,7 @@ my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); # the dump uses single quotes, but we convert any strings containing $creator, # $readers, $writers, to double quoted strings. A wee bit sneaky, but not too # much... -$dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~*(.*?)'/"$1"/g; +$dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g; print $compiled_fh $dumped_data; print $compiled_fh Data::Dumper->Dump([\%groups], [qw(*groups)]) if $GL_BIG_CONFIG and %groups; close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; From f9e5c8b7b2b870668ae992f744ca2a261b145afb Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 18 May 2010 17:50:58 +0530 Subject: [PATCH 368/850] (big-config) doc fixup --- doc/big-config.mkd | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/doc/big-config.mkd b/doc/big-config.mkd index 5ff551f..5965aca 100644 --- a/doc/big-config.mkd +++ b/doc/big-config.mkd @@ -158,14 +158,9 @@ you push the gitolite-admin repo with changes. ### what are the downsides? -There are a few minor issues. +There is one minor issue. -First, "deny" rules (rules whose "permission" is a "-" sign), will not work -exactly the same as before. - -[TODO: add a nice example etc...] - -Second, if you use the delegation feature, you can no longer define or extend +If you use the delegation feature, you can no longer define or extend @groups in a fragment, for security reasons. It will also not let you use any group other than the @fragname itself (specifically, groups which contained a subset of the allowed @fragname, which would work normally, do not work now). From 3ddc8aa0ca70b956595fa9982e5a793105af9743 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 19 May 2010 13:53:18 +0530 Subject: [PATCH 369/850] (important upgrade info here) There has been a format change to the compiled output file. As the CHANGELOG says: Upgrading to v1.5 from any version prior to v1.5 requires an extra step for people who installed gitolite using the "system install / user setup" method described in doc/0-INSTALL.mkd. For such installations, after the administrator has upgraded gitolite system-wide, each "gitolite host" user must run `gl-setup` once (without any arguments). This is *not* an issue if you installed using src/gl-easy-install. --- doc/0-INSTALL.mkd | 12 ++++++++++-- doc/CHANGELOG | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index b1ae760..37c81cc 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -169,10 +169,18 @@ private key to clone and push it. In other words, that person is the administrator for this particular gitolite instance. If your system administrator upgrades gitolite itself, things will usually -just work without any change; you should not have to do anything special. -However, some new features may require additional settings in your +just work without any change; you should not normally have to do anything +special. However, some new features may require additional settings in your `~/.gitolite.rc` file. +> [The only exception to this is when there is a change in the format of the +"compiler" output -- then *each* gitolite hosting user must run "`gl-setup` +again (no arguments needed this time), to fix up the compiled file format. + +> Such a change will be clearly marked with "upgrade notes" both in the +CHANGELOG and in the commit message. For example, this applies when upgrading +to v1.5 or later from any version before it.] + Finally, in the rare case that you managed to lose your keys to the admin repo and want to supply a new pubkey, you can use this command to replace any such key. Could be useful in an emergency -- just get your new "yourname.pub" to diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 743eeca..de1cd01 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,6 +2,25 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] + - v1.5 -- IMPORTANT UPGRADE NOTES below + +Upgrading to v1.5 from any version prior to v1.5 requires an extra step for +people who installed gitolite using the "system install / user setup" method +described in doc/0-INSTALL.mkd. For such installations, after the +administrator has upgraded gitolite system-wide, each "gitolite host" user +must run `gl-setup` once (this time without any arguments). + + - "deny" rules should now work even in "big-config" due to previous change + - proper rule sequencing (required major format change) + + - allow usergroup info to be passed in from outside, say via LDAP; see + doc/big-config.mkd for details + - (new) big-config is now part of mainline (old one had bitrotted); see + doc/big-config.mkd for details + + - gl-system-install: help people simulate an RPM/DEB install by just running + that commmand with appropriate arguments; see doc/0-INSTALL.mkd + - admin-defined commands; see doc/admin-defined-commands.mkd - v1.4.2 (prep for major refactor on rights queries From c993050ef9ed825dc4d9616db546ad1fdff95e69 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 20 May 2010 17:08:21 +0530 Subject: [PATCH 370/850] (minor) doc/3: doc fix on multikeys --- doc/3-faq-tips-etc.mkd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 06f6799..34676aa 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -192,10 +192,10 @@ I have a laptop and a desktop I need to access the server from. I have different private keys on them, but as far as gitolite is concerned both of them should be treated as "sitaram". How does this work? -In gitosis, the admin creates a single "sitaram.pub" containing one line for -each of my pubkeys. In gitolite, we keep them separate: "sitaram@laptop.pub" -and "sitaram@desktop.pub". The part before the "@" is the username, so -gitolite knows these two keys belong to the same person. +The way it works is that you copy one pubkey as "sitaram@laptop.pub" and the +other as "sitaram@desktop.pub". The part before the "@" is the username, so +gitolite knows these two keys belong to the same person. The part after the +"@" can be anything you like, of course; gitolite doesn't care. Note that you don't say "sitaram@laptop" and so on in the **config** file -- as far as the config file is concerned there's just **one** user called From fd85ee2c9169b9d684120199e5121bfcbb9ad90f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 21 May 2010 14:08:05 +0530 Subject: [PATCH 371/850] *try* to make upgrades resilient to format changes (pkg maintainers please read) the commits leading up to v1.5 caused the data format to change (we added a rule sequence number). This in turn caused a problem for people who may have installed using the "system install / user setup" mode of install (which includes people who used RPM/DEB to install it) -- they would now have to *manually* run "gl-setup" once after the rpm/deb upgrade. This commit *tries* to mitigate this problem by recording a data format version number in the compiled output file. On any access to that file, if the version number is not found or is found to be not equal to the current version, gl-setup is run again. The reason I say "*tries*" is that the exact command used to do this is a bit of a hack for now. However, if it works for Fedora and Debian, I'm going to leave it at that :) --- doc/0-INSTALL.mkd | 8 -------- doc/CHANGELOG | 13 ++++++++----- src/gitolite.pm | 9 +++++++++ src/gl-compile-conf | 4 ++++ 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 37c81cc..a110431 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -173,14 +173,6 @@ just work without any change; you should not normally have to do anything special. However, some new features may require additional settings in your `~/.gitolite.rc` file. -> [The only exception to this is when there is a change in the format of the -"compiler" output -- then *each* gitolite hosting user must run "`gl-setup` -again (no arguments needed this time), to fix up the compiled file format. - -> Such a change will be clearly marked with "upgrade notes" both in the -CHANGELOG and in the commit message. For example, this applies when upgrading -to v1.5 or later from any version before it.] - Finally, in the rare case that you managed to lose your keys to the admin repo and want to supply a new pubkey, you can use this command to replace any such key. Could be useful in an emergency -- just get your new "yourname.pub" to diff --git a/doc/CHANGELOG b/doc/CHANGELOG index de1cd01..fdfd39f 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,13 +2,16 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] + - v1.5.1 -- tries to eliminate the need to run gl-setup on data version + change, thus hopefully obsoleting the upgrade note for v1.5 (just below). + - v1.5 -- IMPORTANT UPGRADE NOTES below -Upgrading to v1.5 from any version prior to v1.5 requires an extra step for -people who installed gitolite using the "system install / user setup" method -described in doc/0-INSTALL.mkd. For such installations, after the -administrator has upgraded gitolite system-wide, each "gitolite host" user -must run `gl-setup` once (this time without any arguments). + Upgrading to v1.5 from any version prior to v1.5 requires an extra step + for people who installed gitolite using the "system install / user setup" + method described in doc/0-INSTALL.mkd. For such installations, after the + administrator has upgraded gitolite system-wide, each "gitolite host" user + must run `gl-setup` once (this time without any arguments). - "deny" rules should now work even in "big-config" due to previous change - proper rule sequencing (required major format change) diff --git a/src/gitolite.pm b/src/gitolite.pm index fcc122f..bdc0db8 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -40,6 +40,8 @@ our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG); our %repos; our %groups; +our $data_version; +our $current_data_version = '1.5'; # ---------------------------------------------------------------------------- # convenience subs @@ -253,6 +255,13 @@ sub parse_acl our $gl_user = $ENV{GL_USER}; die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; + unless (defined($data_version) and $data_version eq $current_data_version) { + # this cannot happen for 'easy-install' cases, by the way... + print STDERR "(INTERNAL: $data_version -> $current_data_version; running gl-setup)\n"; + system("$ENV{SHELL} -l gl-setup >&2"); + + die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; + } # basic access reporting doesn't send $repo, and doesn't need to; you just # want the config dumped as is, really diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 63a677c..1d910db 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -103,6 +103,8 @@ my $rule_seq = 0; # multiple times for the same repo+user. So... my %rurp_seen = (); +our $current_data_version; # this comes from gitolite.pm + # catch usernames<->pubkeys mismatches; search for "lint" below my %user_list = (); @@ -372,6 +374,8 @@ for my $fragment_file (glob("conf/fragments/*.conf")) } my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); +my $data_version = $current_data_version; +print $compiled_fh Data::Dumper->Dump([$data_version], [qw(*data_version)]); my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); # the dump uses single quotes, but we convert any strings containing $creator, # $readers, $writers, to double quoted strings. A wee bit sneaky, but not too From 025de395dc72b8ec0ec3e7d4255b566bda039efa Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 21 May 2010 21:32:33 +0530 Subject: [PATCH 372/850] (minor) --- src/gl-auth-command | 6 ++---- src/gl-compile-conf | 7 ++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 80fd393..bcd43b4 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -13,7 +13,6 @@ use warnings; # input: $1 is GL username, plus $SSH_ORIGINAL_COMMAND # output: # security: -# - currently, we just make some basic checks, copied from gitosis # robustness: @@ -168,9 +167,8 @@ if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) { # side notes on detecting a normal git command: the pattern we check allows # old style as well as new style ("git-subcommand arg" or "git subcommand -# arg"), just like gitosis does, although I'm not sure how necessary that is. -# Currently, this is how git sends across the command (including the single -# quotes): +# arg"). Currently, this is how git sends across the command (including the +# single quotes): # git-receive-pack 'reponame.git' my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/); diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 1d910db..f9b5786 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -389,9 +389,7 @@ close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; # any new repos to be created? # ---------------------------------------------------------------------------- -# modern gits allow cloning from an empty repo, so we just create it. Gitosis -# did not have that luxury, so it was forced to detect the first push and -# create it then +# modern gits allow cloning from an empty repo, so we just create it # but it turns out not everyone has "modern" gits :) my $git_version = `git --version`; @@ -469,8 +467,7 @@ for my $repo (keys %repo_config) { # handle gitweb and daemon # ---------------------------------------------------------------------------- -# How you specify gitweb and daemon access is quite different from gitosis. I -# just assume you'll never have any *real* users called "gitweb" or "daemon" +# I just assume you'll never have any *real* users called "gitweb" or "daemon" # :-) These are now "pseduo users" -- giving them "R" access to a repo is all # you have to do From 196b41e0fda70576feaf6d2eb2897e901e44bd45 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 21 May 2010 17:53:05 +0530 Subject: [PATCH 373/850] *major* doc revamp people will NOT read documentation, especially the bloody install documentation. I'm about ready to throw in the towel and declare gitolite unsupported, take-it-or-leave-it. But I'm making one last attempt to refocus the install doc to better suit the "I know I'm very smart and I dont have to read docs so it's clearly your fault that I am not able to install gitolite" crowd. As a bonus, though, I ended up making proper, hyper-linked, TOCs for most of the docs, and moved a whole bunch of stuff around. Also finally got some of the ssh stuff over from my git-notes repo because it really belongs here. --- README.mkd | 111 ++++--- contrib/autotoc | 28 ++ doc/0-INSTALL.mkd | 494 +++++++++++--------------------- doc/2-admin.mkd | 64 +++-- doc/3-faq-tips-etc.mkd | 240 +++++++++------- doc/4-wildcard-repositories.mkd | 46 ++- doc/5-delegation.mkd | 21 +- doc/6-ssh-troubleshooting.mkd | 118 ++++++-- doc/7-install-transcript.mkd | 28 +- doc/9-gitolite-and-ssh.mkd | 155 ++++++++++ doc/9-packaging.mkd | 56 ++++ doc/9-uninstall.mkd | 67 +++++ doc/TODO | 3 - doc/admin-defined-commands.mkd | 28 +- doc/big-config.mkd | 20 +- doc/progit-article.mkd | 8 +- 16 files changed, 930 insertions(+), 557 deletions(-) create mode 100755 contrib/autotoc create mode 100644 doc/9-gitolite-and-ssh.mkd create mode 100644 doc/9-packaging.mkd create mode 100644 doc/9-uninstall.mkd delete mode 100644 doc/TODO diff --git a/README.mkd b/README.mkd index b0793d4..1f6c93e 100644 --- a/README.mkd +++ b/README.mkd @@ -1,60 +1,92 @@ # gitolite -> [Update 2009-10-28: apart from all the nifty new features, there's now an -> "easy install" script in the src directory. This script can be used to -> install as well as upgrade a gitolite install. Please see the INSTALL -> document for details] +Gitolite is an access control layer on top of git, which allows access control +down to the branch level, including specifying who can and cannot *rewind* a +given branch. ---- -Gitolite is a rewrite of gitosis, with a completely different config file that -allows (at last!) access control down to the branch level, including -specifying who can and cannot *rewind* a given branch. - In this document: - * what - * why - * extra features - * security - * contact and license + * what + * why + * other features + * security + * contact and license ---- + + ### what Gitolite allows a server to host many git repositories and provide access to -many developers, without having to give them real userids on the server. The -essential magic in doing this is ssh's pubkey access and the `authorized_keys` -file, and the inspiration was an older program called gitosis. +many developers, without having to give them real userids on or shell access +to the server. The essential magic in doing this is ssh's pubkey access and +the `authorized_keys` file, and the inspiration was an older program called +gitosis. Gitolite can restrict who can read from (clone/fetch) or write to (push) a repository. It can also restrict who can push to what branch or tag, which is very important in a corporate environment. Gitolite can be installed without requiring root permissions, and with no additional software than git itself and perl. It also has several other neat features described below and -elsewhere in the `doc/` directory. +elsewhere in the [doc/][docs] directory. + + ### why -I have been using gitosis for a while, and have learnt a lot from it. But in -a typical $DAYJOB setting, there are some issues: +Gitolite is separate from git, and needs to be installed and configured. So... +why do we bother? - * it's not always Linux; you can't just "urpmi gitosis" (or yum or apt-get) - and be done - * often, "python-setuptools" isn't installed (and on a Solaris9 I was trying - to help remotely, we never did manage to install it eventually) - * you don't have root access, or the ability to add users (this is also true - for people who have just one userid on a hosting provider) - * the most requested feature (see below) had to be written anyway +Gitolite is useful in any server that is going to host multiple git +repositories, each with many developers, where some sort of access control is +required. -All of this pointed to a rewrite. In perl, naturally :-) +In theory, this can be done with plain old Unix permissions: each user is a +member of one or more groups, each group "owns" one or more repositories, and +using unix permissions (especially the setgid bit -- `chmod g+s`) you can +allow/disallow users access to repos. -### extra features +But there are several disadvantages here: + + * every user needs a userid and password on the server. This is usually a + killer...! + * adding/removing access rights involves complex `usermod -G ...` mumblings + which most admins would rather not deal with, thanks to you-know-who + * *viewing* (aka auditing) the current set of permissions requires running + multiple commands to list directories and their permissions/ownerships, + users and their group memberships, and then correlating all these manually + * auditing historical permissions or permission changes is pretty much + impossible without extraneous tools + * errors or omissions in setting the permissions exactly can cause problems + of either kind: false accepts or false rejects + * without going into ACLs it is not possible to give someone read-only + access to a repo; they either get read-write access or no access + * it is absolutely impossible to restrict pushing by branch name or tag + name. + +Gitolite does away with all this: + + * it uses ssh magic to remove the need to give actual unix userids to + developers + * it uses a simple but powerful config file format to specify access rights + * access control changes are affected by modifying this file, adding or + removing user's public keys, and "compiling" the configuration + * this also makes auditing trivial -- all the data is in one place, and + changes to the configuration are also logged, so you can audit them. + * finally, the config file allows distinguishing between read-only and + read-write access, not only at the repository level, but at the branch + level within repositories. + + + +### other features The most important feature I needed was **per-branch permissions**. This is pretty much mandatory in a corporate environment, and is almost the single -reason I started *thinking* about rolling my own gitosis in the first place. +reason I started *thinking* about writing gitolite. It's not just "read-only" versus "read-write". Rewinding a branch (aka "non fast forward push") is potentially dangerous, but sometimes needed. So is @@ -63,28 +95,27 @@ something in between allowing anyone to do it (the default) and disabling it completely (`receive.denyNonFastForwards` or `receive.denyDeletes`). Here're **some more features**. All of them, and more, are documented in -detail [here][gsdiff]. +detail somewhere in gitolite's [doc/][docs] subdirectory. -[gsdiff]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#diff - - * simpler, yet far more powerful, config file syntax, including specifying + * simple, yet powerful, config file syntax, including specifying gitweb/daemon access. You'll need this power if you manage lots of users+repos+combinations of access * apart from branch-name based restrictions, you can also restrict by file/dir name changed (i.e., output of `git diff --name-only`) - * config file syntax gets checked upfront, and much more thoroughly * if your requirements are still too complex, you can split up the config file and delegate authority over parts of it - * easier to specify gitweb owner, description and gitweb/daemon access - * easier to sync gitweb (http) authorisation with gitolite's access config - * more comprehensive logging [aka: management does not think "blame" is just - a synonym for "annotate" :-)] + * easy to specify gitweb owner, description and gitweb/daemon access + * easy to sync gitweb (http) authorisation with gitolite's access config + * comprehensive logging [aka: management does not think "blame" is just a + synonym for "annotate" :-)] * "personal namespace" prefix for each dev * migration guide and simple converter for gitosis conf file * "exclude" (or "deny") rights at the branch/tag level * specify repos using patterns (patterns may include creator's name) * define powerful operations on the server side, even github-like forking + + ### security Due to the environment in which this was created and the need it fills, I @@ -105,9 +136,13 @@ details, looking for the word "security". ---- + + ### contact and license Gitolite is released under GPL v2. See COPYING for details. sitaramc@gmail.com mailing list: gitolite@googlegroups.com + +[docs]: http://github.com/sitaramc/gitolite/blob/pu/doc diff --git a/contrib/autotoc b/contrib/autotoc new file mode 100755 index 0000000..36a6f61 --- /dev/null +++ b/contrib/autotoc @@ -0,0 +1,28 @@ +#!/usr/bin/perl -w + +# filter gitolite's mkd files through this; it's designed to be idempotent of +# course + +undef $/; +$doc = <>; + +$doc =~ s/^<\/a>\n\n//mg; + +@toc = $doc =~ /^###.*/mg; +$i = 0; +$doc =~ s/^###/$i++; "<\/a>\n\n###"/mge; + +$i = 0; +for (@toc) { + $i++; + s/### (.*)/ * $1<\/a>/; + s/^(#+)/' ' x length($1)/e; +} + +$toc = "In this document:\n\n"; +$toc .= join("\n", @toc); +$toc .= "\n\n"; + +$doc =~ s/^In this document:\n\n.*?\n\n/$toc/sm; + +print $doc; diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index a110431..c21d03c 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -1,193 +1,174 @@ -# installing gitolite - -[Update 2009-11-18: easy install now works from msysgit also!] - -Gitolite is somewhat unusual as far as "server" software goes -- every userid -on the server is a potential "gitolite host". - -This document tells you how to install gitolite. After the install is done, -you may want to see [doc/2-admin.mkd][admin] for adding users, repos, etc. - -**Please note** that gitolite depends heavily on proper ssh setup and pubkey -based access. Sadly, most people don't know ssh as well as they think they -do. To make matters worse, ssh problems in gitolite don't always look like -ssh problems. Please read about [ssh troubleshooting][doc6] if you have *any* -kind of trouble installing gitolite! - -[admin]: http://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd -[doc6]: http://github.com/sitaramc/gitolite/blob/pu/doc/6-ssh-troubleshooting.mkd +# gitolite installatation In this document: - * install methods - * user install - * typical example run - * advantages over the older install methods - * disadvantages - * upgrades - * other notes - * system install / user setup - * multiple gitolite instances - * next steps - * appendix A: server and client requirements for user install - * server - * install workstation - * admin workstation(s) - * appendix B: uninstalling gitolite - * appendix C: NOTE TO PACKAGE MAINTAINERS + * please read this first + * important notes + * conventions used + * requirements + * client side + * server side + * installation and setup + * (package method) directly on the server, using RPM/DEB + * (root method) directly on the server, manually, with root access + * (non-root method) directly on the server, manually, without root access + * (from-client method) install from the client to the server + * special cases -- multiple gitolite instances + * upgrading + * uninstalling + * cleaning out a botched install + * uninstalling gitolite completely ---- -### install methods + -There are 2 ways to install gitolite: The **user-install** mode was the -traditional way, and is used when *any* of the following is true: +### please read this first - * you don't have root on your "server" (some types of hosting setups, many - corporate paranoia setups ;-) - * your server distro does not have gitolite in its package repositories - * your server distro's package repositories have an old version of gitolite - * you want to stay current with the latest gitolite versions - * your server is not Linux (maybe AIX, or Solaris, etc.) + -The "user install" section describes this method. +#### important notes -The **system-install followed by user-setup** mode is used when you (or -someone who has root) has installed an RPM or DEB of gitolite and you intend -to use that version. [Update 2010-04-27: there is now a script called -`gl-system-install` that helps you do a system-install if your distribution -does not yet have a package for gitolite; details below]. +Please make sure you understand the following points first. -The "system install / user setup" section describes this method. + * gitolite is somewhat unusual as far as "server" software goes -- every + userid on the server is a potential "gitolite host". ----- + * gitolite depends **heavily** on ssh pubkey (passwordless) access. Do not + assume you know all about ssh -- most people **don't**. If in doubt, use + a dedicated userid on both client and server for installation and + administration of gitolite. -### user install + To make matters worse, ssh problems in gitolite don't always look like ssh + problems. See [doc/6-ssh-troubleshooting.mkd][doc6] for help. -The `gl-easy-install` script makes installing very easy for this case. **This -script will setup everything on the server, but you have to run it on your -workstation, NOT on the server!** + -Assumptions/pre-requisites: +#### conventions used - * you have a server to host gitolite - * git is installed on that server (and so is perl) - * you have a userid on that server - * you have ssh-pubkey (**password-less**) login to that userid - * if you have only password access, run `ssh-keygen -t rsa` to create a - new keypair if needed, then run `ssh-copy-id user@host`. If you do - not have `ssh-copy-id`, read doc/3-faq-tips-etc.mkd and look for - `ssh-copy-id` in that file for instructions - * you have a clone or an archive of gitolite somewhere on your workstation - * if you don't have one, just run `git clone git://github.com/sitaramc/gitolite` - * your workstation has bash (even msysgit bash will do) +We assume the admin user is "sitaram", and his workstation is called "client". +The hosting user is "git", and the server is called "server". Substitute your +values as needed. -Once you have all this, just `cd` to that clone and run `src/gl-easy-install` -and follow the prompts! (Running it without any arguments shows you usage -plus other useful info). + -#### typical example run +#### requirements -A typical run for me is: + - src/gl-easy-install -q git my.git.server sitaram +##### client side -`-q` stands for "quiet" mode -- very minimal output, no verbose descriptions -of what it is going to do, and no pauses unless absolutely needed. However, -if you're doing this for the first time or you appreciate knowing what it is -actually doing, I suggest you skip the `-q`. + * git version 1.6.2 or greater + * even msysgit on Windows is fine; please don't ask me for help if + you're using putty, plink, puttygen, etc., for ssh; I recommend + msysgit for Windows and the openssh that comes with it + * if you're using the "from-client" method of install (see below), the bash + shell is needed + * again, msysgit on Windows is fine -A detailed transcript of an install done using this method can be found in -[doc/7-install-transcript.mkd][7it]. + -[7it]: http://github.com/sitaramc/gitolite/blob/pu/doc/7-install-transcript.mkd +##### server side -#### advantages over the older install methods + * any Unix system with a posix compatible "sh". + * people using "csh" or derivatives please don't ask me for help -- tell + your admin csh is not posix compatible + * git version 1.6.2 or greater + * can be in a non-PATH location if you are unable to install it + normally; see the `$GIT_PATH` variable in the "rc" file + * perl (but since git requires it anyway, you probably have it) + * openssh or any ssh that can understand the `authorized_keys` file format - * all ssh problems reduced to **just one pre-requisite**: enable ssh pubkey - (password-less) access to the server from your workstation first - * the script takes care of all the server side work - * when done: - * you get two different pubkeys (the original one for command line - access as before, plus a new one, created by the script, for gitolite - access) - * you can admin gitolite by commit+push a "gitolite-admin" repo, just - like gitosis (i.e., full "push to admin" power!) + -#### disadvantages +### installation and setup - * need a recent bash + -#### upgrades +#### (package method) directly on the server, using RPM/DEB -Upgrading gitolite is easy. + * from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server. + Put it in `/tmp/sitaram.pub`. -To upgrade, pull the latest "master" (or other) branch in your gitolite repo -clone, then run the same exact command you ran to do the install, except you -can leave out the last argument. + * (U) on the server, as root, do the install (urpmi, yum, apt-get, etc.). -And you might want to add a `-q` to speed things up :-) + * on the server, "su - git", then as "git" user, run `gl-setup + /tmp/sitaram.pub`. -Note that this only upgrades the software. Unlike earlier versions, it does -**not** touch the `conf/gitolite.conf` file or the contents of `keydir` in any -way. I decided that it is not possible to **safely** let an upgrade do -something meaningful with them -- fiddling with existing config files (as -opposed to merely creating one which did not exist) is best left to a human. + * on the client, run `cd; git clone git@server:gitolite-admin` -#### other notes + - * if you run `src/gl-easy-install` without the `-q` option, you will be - given a chance to edit `~/.gitolite.rc`. You can change any options (such - as paths, for instance), but be sure to keep the perl syntax -- you - *don't* have to know perl to do so, it's fairly easy to guess in this - limited case. +#### (root method) directly on the server, manually, with root access ----- + * from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server. + Put it in `/tmp/sitaram.pub`. -### system install / user setup + * (U) on the server, as root, do the following: -(Note: other than getting a public key from the admin's workstation, -everything in this mode of install happens on the server). + cd $HOME + git clone git://github.com/sitaramc/gitolite gitolite-source + cd gitolite-source + mkdir -p /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks + src/gl-system-install /usr/local/bin /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks -In this mode, the software is first installed system-wide, then the user runs -a command to set up the hosting, supplying a public key. + * on the server, "su - git", then as "git" user, run `gl-setup + /tmp/sitaram.pub`. -The root user can **install the software system-wide**, using either a package -(rpm, deb, etc.), or by using the `gl-system-install` script. (Run the script -without any arguments for a brief usage message. If that's not clear enough, -read Appendix C below for details.) + * on the client, run `cd; git clone git@server:gitolite-admin` -A normal user can then **set up the hosting** by running a command like this: + - gl-setup adminname.pub +#### (non-root method) directly on the server, manually, without root access -where adminname.pub is a copy of a public key from that user's workstation. +WARNING: if you use this method you'd better know enough about ssh to be able +to keep your keys straight, and you'd also better have password access to the +server so that if you screw up the keys you can still get on, or be able to +"su - git" from some other user on the server. -The first time this is run, it will create a "gitolite-admin" repo and -populate it with the right configuration for whoever has the corresponding -private key to clone and push it. In other words, that person is the -administrator for this particular gitolite instance. + * from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server. + Put it in `/tmp/sitaram.pub`. -If your system administrator upgrades gitolite itself, things will usually -just work without any change; you should not normally have to do anything -special. However, some new features may require additional settings in your -`~/.gitolite.rc` file. + * (U) on the server, as "git", do the following: -Finally, in the rare case that you managed to lose your keys to the admin repo -and want to supply a new pubkey, you can use this command to replace any such -key. Could be useful in an emergency -- just get your new "yourname.pub" to -the server and run the same command as above. + cd $HOME + git clone git://github.com/sitaramc/gitolite gitolite-source + cd gitolite-source + mkdir -p $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks + src/gl-system-install $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks -**IMPORTANT**: there are two variables in the `~/.gitolite.rc` file: -`$GL_PACKAGE_CONF` and `$GL_PACKAGE_HOOKS`. If you remove or change either of -them, expect trouble :-) + * on the server, still as "git", run `gl-setup /tmp/sitaram.pub`. -#### multiple gitolite instances + * if `$HOME/bin` is not on the default PATH, fiddle with your `.bashrc` or + `.bash_profile` files and add it somehow. You can also try adding + `$GIT_PATH = "$ENV{HOME}/bin";` to `~/.gitolite.rc` on the server. -With this mode of install, it's trivial to create multiple gitolite instances -(say one for each department, on some mega company-wide server). You can even -do this without giving shell access to the admins. Here's an example with -just two "departments", and their admins Alice and Bob: + * on the client, run `cd; git clone git@server:gitolite-admin` + + + +#### (from-client method) install from the client to the server + +This used to be the most common install method for a long time, and it is +still the one I use for most of my testing. The **main advantage** of this +method is that it **forces you** to solve the ssh pubkey problem **before** +attempting to install. Sadly, it also forces the admin to use a **different** +URL for gitolite repos than normal users, which seems to confuse a heck of a +lot of people who don't read the prominently displayed messages and/or the +documentation. + +This method is verbosely documented in [doc/7-install-transcript.mkd][doc7], +including *outputs* of the commands concerned. + + + +### special cases -- multiple gitolite instances + +With the first two methods (package or root methods) of installation, it's +trivial to create multiple gitolite instances (say one for each department, on +some mega company-wide server). You can even do this without giving shell +access to the admins. Here's an example with just two "departments", and +their admins Alice and Bob: * create userids `webbrowser_repos` and `webserver_repos` * ask Alice and Bob for their pubkeys; copy them to the respective home @@ -204,195 +185,52 @@ to have a command line on the server, so don't give them the passwords if you don't need to -- the pubkey will allow them to be gitolite admins on their domain, and that's quite enough for normal operations. ----- + -### next steps +### upgrading -If you installed it using "gl-easy-install", the last message produced by that -script should tell you how to add users, repos, etc. In any case, you will -find more details in [doc/2-admin.mkd][admin]. +Upgrading gitolite is easy. In each method above, just re-do the step that is +marked "(U)". Also, if you're using either of the two methods that use the +`src/gl-system-install` command, please make sure you give it the same +arguments! + +Also, remember that some new features may require additional settings in your +`~/.gitolite.rc` file. + + + +### uninstalling + + + +#### cleaning out a botched install + +When people have trouble installing gitolite, they often try to change a bunch +of things manually on the server. This usually makes things worse ;-) so +here's how to clean the slate. + + * client-side + * edit `~/.ssh/config` and delete the paragraph starting with `host + gitolite`, if present. + * remove `~/gitolite-admin` + * server-side + * edit `~/.ssh/authorized_keys` and delete all lines between `# gitolite + start` and `# gitolite end` inclusive. + * remove `~/.gitolite`, `~/.gitolite.rc` and + `~/repositories/gitolite-admin.git` + + + +#### uninstalling gitolite completely + +There's some duplication between this and the previous section, but +uninstalling gitolite is described in great detail in +[doc/9-uninstall.mkd][doc9unin] ---- - +[doc6]: http://github.com/sitaramc/gitolite/blob/pu/doc/6-ssh-troubleshooting.mkd +[doc7]: http://github.com/sitaramc/gitolite/blob/pu/doc/7-install-transcript.mkd +[doc9unin]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-uninstall.mkd +[doc9ssh]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-ssh-tips.mkd -### appendix A: server and client requirements for user install - -There are 3 machines *potentially* involved in installing and administering -gitolite. - -#### server - -This is where gitolite is eventually installed. You need a *normal* userid -(typically "git" but can be anything) on this machine; root access is *not* -needed, but it has to be some sort of Unix (not Windows). - -You need the following software on it: - - * git - * can be in a non-PATH location if you are unable to install it - normally; see the `$GIT_PATH` variable in the "rc" file - * perl - * default install is fine; no special modules are needed - * (a normal install of git also requires/installs perl, so you probably - have it already) - * openssh server - * (I guess any ssh server that can understand the `authorized_keys` file - format should work) - -#### install workstation - -Installing or upgrading the gitolite software itself is best done by running -the easy-install program from a gitolite clone. - -This script is heavily dependent on bash, so you need a machine with a bash -shell. Even the bash that comes with msysgit is fine, if you don't have a -Linux box handy. - -If you have neither Linux nor Windows+msysgit, you still have a few -alternatives: - - * use a different userid on the same server (assuming it has bash) - * use the same userid on the same server (same assumption) - * manually simulate the script directly on the server (doable, but tedious) - -#### admin workstation(s) - -When you install gitolite, it creates a repository called "gitolite-admin" and -gives you permissions on it. - -Administering gitolite (adding repos/users, assigning permissions, etc) is -done by cloning this repo, making changes to a file called -`conf/gitolite.conf`, adding users' pubkeys to `keydir/`, and pushing the -changes back to the server. - -Which means all this can be done from *any* machine. You'll normally do it -from the same machine you used to install gitolite, but it doesn't have to be -the same one, as long as your pubkey has been added and permissions given to -allow you to push to the gitolite-admin repo. - - - -### appendix B: uninstalling gitolite - -Sometimes you might find gitolite is overkill -- you have only one user -(yourself) pushing maybe. Or maybe gitolite is just not enough -- you want a -web-based front end that users can use to manage their keys themselves, etc., -in which case you'd probably switch to [github][g1], [girocco][g2], -[indefero][g3] or [gitorious][g4]. [Gerrit][g5] is quite nice too, if you -want collaborative code review there's nothing like it. Either way, you'd -like to uninstall gitolite. - -[g1]: http://github.com -[g2]: http://repo.or.cz/w/girocco.git -[g3]: http://www.indefero.net/ -[g4]: http://gitorious.com/ -[g5]: http://code.google.com/p/gerrit/ - -Uninstalling gitolite is fairly easy, although it is manual. (We'll assume -`$REPO_BASE` in the rc file was left at its default of `~/repositories`; if -not, adjust accordingly): - -**server side tasks** - - * edit `~/.ssh/authorized_keys` and delete the `# gitolite start` and `# - gitolite end` markers and all the lines between them. This will prevent - any of your users from attempting a push while you are doing this. - - If you are the only user, and/or *need* one or more of those keys to - continue to access this account (like if one of them is your laptop or - your home desktop etc.) then instead of deleting the line you can just - delete everything upto but not including the words "ssh-rsa" or "ssh-dss". - - * Now remove (or move aside or rename to something else if you're paranoid) - the following files and directories. - - ~/.gitolite - ~/.gitolite.rc - ~/repositories/gitolite-admin.git - - * You can remove all of `~/repositories` if you have not really started - using gitolite properly yet; that's your choice. - - If you *do* need to preserve the other repos and continue to use them, - remove all the `update` hooks that git installs on each repository. The - easiest way is: - - find ~/repositories -wholename "*.git/hooks/update" | xargs rm -f - - but you can do it manually if you want to be careful. - -**client side tasks** - - * Any remote users that still have access must update their clone's remote - URLs (edit `.git/config` in the repo) to prefix `repositories/` before the - actual path used, in order for the remote to still work. This is because - you'll now be accessing it through plain ssh, which means you have to give - it the full path. - - * Finally, you as the gitolite admin will probably have a host stanza for - "gitolite" in your *client*'s `~/.ssh/config`. Find and delete lines that - look like this: - - host gitolite - user git - hostname your.server - port 22 - identityfile ~/.ssh/your-gitolite-admin-username - -### appendix C: NOTE TO PACKAGE MAINTAINERS - -Here's how you'd package gitolite. In the following description, location "X" -can be, say, `/usr/share/gitolite/conf` or some such, and similarly location -"Y" can be perhaps `/usr/share/gitolite/hooks`. It's upto your distro -policies where they are. - -**Step 1**: Clone the gitolite repo and run the make command inside the clone - - git clone git://github.com/sitaramc/gitolite.git - cd gitolite - make pu.tar # or "make master.tar" or "make v1.2.tar" etc - -Then you explode the tar file in some temporary location. - -*Alternatively, you can `git checkout` the tag or branch you want, and run -this command in the clone directly*: - - git describe --tags --long > conf/VERSION - -**Step 2**: Now make the following changes (no trailing slashes in the -location values please): - - * `src/gl-setup` should have the following line: - - GL_PACKAGE_CONF="X" - - * `conf/example.gitolite.rc` should have the following lines: - - $GL_PACKAGE_CONF="X"; - $GL_PACKAGE_HOOKS="Y"; - - * delete `src/gl-easy-install`; that script is meant for a totally different - mode of installation and does *not* play well in this mode :-) - -**Step 3**: Move (or arrange to move) the files to their proper locations as -given below: - - * everything in "src" goes somewhere on the PATH - * everything in "conf" goes to location "X" - * everything in "hooks" goes to location "Y" - -**Step 4**: There is no step 4. Unless you count telling your users to run -`gl-setup` as a step :) - -On the initial install (urpmi, yum install, or apt-get install), you could -also choose to setup a userid called "gitolite", and run "gl-setup" as that -user; however I do not know how you would come up with the initial pubkey that -is needed. Anyway, the point is that the "gitolite" user is no more special -than any other in terms of hosting gitolite. Any user can host gitolite on -his userid by just running "gl-setup". - -When you upgrade, just overwrite all the files; it'll all just work. In fact, -other than the initial "gl-setup" run, the only time a gitolite hosting user -has to actually do anything is to edit their own `~/.gitolite.rc` file if they -want to enable or disable specific features. diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 8eeaf2a..1770453 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -5,37 +5,39 @@ In this document: - * administer - * adding users and repos - * moving pre-existing repos into gitolite - * specifying gitweb and daemon access - * custom hooks - * hook chaining - * custom git config + * please read this first + * adding users and repos + * other features + * moving pre-existing repos into gitolite + * specifying gitweb and daemon access + * custom hooks + * hook chaining + * custom git config -### administer +---- -First of all, ***do NOT add new repos manually***, unless you know how to add -the required hook as well. Without the hook, branch-level access control will -not work for that repo, which sorta defeats the idea of using gitolite :-) + -Most normal (day-to-day) gitolite admin work is done by cloning the -gitolite-admin repo from the server to your workstation, making changes to the -clone, and pushing those changes back. +### please read this first -If you installed using "gl-easy-install", that script would have already tried -to clone the repo to your `$HOME`. Otherwise clone it yourself to any -convenient location you like. + * ***do NOT add new repos manually***, unless you know how to add the + required hook as well. Without the hook, branch-level access control will + not work for that repo, which sorta defeats the idea of using gitolite :-) + + * most normal (day-to-day) gitolite admin work is done by cloning the + gitolite-admin repo from the server to your workstation, making changes to + the clone, and pushing those changes back. The installation steps in + [doc/0-INSTALL.mkd][doc0] include the steps to do this clone if needed. Once you've cloned it, you're ready to add users and repos. -#### adding users and repos + + +### adding users and repos * ask each user who will get access to send you a public key. See other sources (for example [here][genpub]) for how to do this -[genpub]: http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key - * rename each public key according to the user's name, with a `.pub` extension, like `sitaram.pub` or `john-smith.pub`. You can also use periods and underscores @@ -48,7 +50,15 @@ Once you've cloned it, you're ready to add users and repos. and give them permissions as required. The users names should be exactly the same as their keyfile names, but without the `.pub` extension - * when done, commit your changes and push + * when done, commit your changes and push. Any new repos you specified will + automatically be created (empty, but clonable) and users' access will be + updated as needed. + + + +### other features + + #### moving pre-existing repos into gitolite @@ -69,6 +79,8 @@ gitolite: `conf/gitolite.conf` in your gitolite-admin repo clone. Then add, commit, push. + + #### specifying gitweb and daemon access This is a feature that I personally do not use (corporate environments don't @@ -105,6 +117,8 @@ The "compile" script will keep these files consistent with the config settings -- this includes removing such settings/files if you remove "read" permissions for the special usernames or remove the description line. + + #### custom hooks You can supply your own, custom, hook scripts if you wish. Just put a @@ -122,6 +136,8 @@ implements all the branch-level permissions in gitolite. If you fiddle with the hooks directory, please make sure you do not mess with this file accidentally, or all your fancy per-branch permissions will stop working.** + + #### hook chaining Gitolite basically takes over the update hook for all repos, but some setups @@ -143,6 +159,8 @@ Finally, these names (`update.secondary` and `post-update.secondary`) are merely the defaults. You can change them to anything you want; look in conf/example.gitolite.rc for details. + + #### custom git config The custom hooks feature is a blunt instrument -- all repos get the hook you @@ -164,3 +182,7 @@ basic forms of the "git config" command: It does not (currently) support other options like `--add`, the `value_regex`, etc. + +[doc0]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd +[genpub]: http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key + diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 34676aa..19670ce 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -2,43 +2,46 @@ In this document: - * common errors and mistakes - * git version dependency - * other errors, warnings, notes... - * ssh-copy-id - * cloning an empty repo - * `@all` syntax for repos - * umask setting - * getting a tar file from a clone - * features - * syntax and normal usage - * simpler syntax - * one user, many keys - * security, access control, and auditing - * two levels of access rights checking - * better logging - * "exclude" (or "deny") rules - * separating delete and rewind rights - * file/dir NAME based restrictions - * delegating parts of the config file - * convenience features - * what repos do I have access to? - * error checking the config file - * including config lines from other files - * support for git installed outside default PATH - * "personal" branches - * custom hooks and custom git config - * helping with gitweb - * easier to specify gitweb "description" and gitweb/daemon access - * easier to link gitweb authorisation with gitolite - * advanced features - * repos named with wildcards - * admin defined commands - * access control for external commands - * design choices - * keeping the parser and the access control separate + * common errors and mistakes + * git version dependency + * other errors, warnings, notes... + * cloning an empty repo + * `@all` syntax for repos + * umask setting + * getting a tar file from a clone + * features + * syntax and normal usage + * simpler syntax + * one user, many keys + * security, access control, and auditing + * two levels of access rights checking + * better logging + * "exclude" (or "deny") rules + * separating delete and rewind rights + * file/dir NAME based restrictions + * delegating parts of the config file + * convenience features + * what repos do I have access to? + * including config lines from other files + * support for git installed outside default PATH + * "personal" branches + * custom hooks and custom git config + * helping with gitweb + * easier to specify gitweb "description" and gitweb/daemon access + * easier to link gitweb authorisation with gitolite + * advanced features + * repos named with wildcards + * admin defined commands + * access control for external commands + * svnserve + * design choices + * keeping the parser and the access control separate -## common errors and mistakes +---- + + + +### common errors and mistakes * adding `repositories/` at the start of the repo name in the `git clone`. This error is typically made by the *admin* himself -- because he knows @@ -60,36 +63,19 @@ In this document: Please see doc/6-ssh-troubleshooting.mkd for what all this means. -## git version dependency + + +### git version dependency Gitolite (on the server) now refuses to run if git is not at least 1.6.2. -## other errors, warnings, notes... + -### ssh-copy-id +### other errors, warnings, notes... -don't have `ssh-copy-id`? This is broadly what that command does, if you want -to replicate it manually. The input is your pubkey, typically -`~/.ssh/id_rsa.pub` from your client/workstation. + - * it copies it to the server as some file - - * it appends that file to `~/.ssh/authorized_keys` on the server - (creating it if it doesn't already exist) - - * it then makes sure that all these files/directories have go-w perms - set (assuming user is "git"): - - /home/git/.ssh/authorized_keys - /home/git/.ssh - /home/git - -[Actually, `sshd` requires that even directories *above* `~` (`/`, `/home`, -typically) also must be `go-w`, but that needs root. And typically -they're already set that way anyway. (Or if they're not, you've got -bigger problems than gitolite install not working!)] - -### cloning an empty repo +#### cloning an empty repo Cloning an empty repo is only possible with clients greater than 1.6.2. So at least one of your clients needs to have a recent git. Once at least one @@ -100,7 +86,9 @@ end hung up unexpectedly`. However, you can ignore this, since it doesn't seem to hurt anything. [Update 2009-09-14; this has been fixed in git 1.6.4.3] -### `@all` syntax for repos + + +#### `@all` syntax for repos There *is* a way to use the `@all` syntax for repos also, as described in `conf/example.conf`. However, there are a couple of minor cautions: @@ -110,12 +98,16 @@ There *is* a way to use the `@all` syntax for repos also, as described in access, we do not support this. * don't try giving `@all` users some permission for `@all` repos -### umask setting + + +#### umask setting Gitweb not able to read your repos? You can change the umask for newly created repos to something more relaxed -- see the `~/.gitolite.rc` file -## getting a tar file from a clone + + +### getting a tar file from a clone You can clone the repo from github or indefero, then execute a make command to extract a tar file of the branch you want. Please use the make command, not a @@ -131,17 +123,23 @@ plain "git archive", because the Makefile adds a file called -## features + + +### features Apart from the big ones listed in the top level README, and subjective ones like "better config file format", gitolite has evolved to have many useful -fearures than the original goal of "gitosis + branch-level access control". +features than the original goal of branch-level access control. -### syntax and normal usage + + +#### syntax and normal usage -#### simpler syntax + + +##### simpler syntax The basic syntax is simpler and cleaner but it goes beyond that: **you can specify access in bits and pieces**, even if they overlap. @@ -186,7 +184,9 @@ See the "specify gitweb/daemon access" section below for one more example. -#### one user, many keys + + +##### one user, many keys I have a laptop and a desktop I need to access the server from. I have different private keys on them, but as far as gitolite is concerned both of @@ -229,16 +229,19 @@ corresponding derived usernames (which is what goes into the sitaramc@gmail.com@laptop.pub sitaramc@gmail.com sitaramc@gmail.com@desktop.pub sitaramc@gmail.com -### security, access control, and auditing + + +#### security, access control, and auditing -#### two levels of access rights checking + + +##### two levels of access rights checking Gitolite has two levels of access checks. The **first check** is what I will -call the **pre-git** level (this is the only check that gitosis has). At this -stage, the `gl-auth-command` has been invoked by `sshd`, and it knows just -three things: +call the **pre-git** level. At this stage, the `gl-auth-command` has been +invoked by `sshd`, and it knows just three things: * who, * what repository, and @@ -268,7 +271,9 @@ any of the refexes match, the push succeeds. If none of them match, it fails. Gitolite also allows "exclude" or "deny" rules. See later in this document for details. -#### better logging + + +##### better logging If you have been too liberal with the permission to rewind, it has built-in logging as an emergency fallback if someone goes too far, or for audit @@ -294,7 +299,9 @@ The other parts of the log line are the name of the repo, the refname being updated, the user updating it, and the refex pattern (from the config file) that matched, in case you need to debug the config file itself. -#### "exclude" (or "deny") rules + + +##### "exclude" (or "deny") rules Here is an illustrative explanation of "deny" rules. However, please be sure to read the "DENY/EXCLUDE RULES" section in `conf/example.conf` for important @@ -343,7 +350,9 @@ And here's how it works: before the third one, and it has a `-` as the permission, so the push fails -#### separating delete and rewind rights + + +##### separating delete and rewind rights Since the beginning, `RW+` meant being able to rewind *or* delete a ref. My stand is that these two are fairly similar, and infact a rewind is almost the @@ -380,7 +389,9 @@ message when you push, a non-existant user. [sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c# -#### file/dir NAME based restrictions + + +##### file/dir NAME based restrictions In addition to branch-name based restrictions, gitolite also allows you to restrict what files or directories can be involved in changes being pushed. @@ -389,18 +400,24 @@ changed, treating each filename as a "ref" to be matched. Please see `conf/example.conf` for syntax and examples. -#### delegating parts of the config file + + +##### delegating parts of the config file You can now split up the config file and delegate the authority to specify access control for their own pieces. See [doc/5-delegation.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/5-delegation.mkd) for details. -### convenience features + + +#### convenience features -#### what repos do I have access to? + + +##### what repos do I have access to? Sometimes there are too many repos, maybe even named similarly, or with the potential for typos, confusion about hyphens/underscores or upper/lower case, @@ -431,20 +448,15 @@ administrator can also say things like: to get this info for other user(s). -#### error checking the config file + -gitosis does not do any. I just found out that if you mis-spell `members` as -`member`, gitosis will silently ignore it, and leave you wondering why access -was denied. - -Gitolite "compiles" the config file first and keyword typos *are* caught so -you know right away. - -#### including config lines from other files +##### including config lines from other files See the entry under "INCLUDE SOME OTHER FILE" in `conf/example.conf`. -#### support for git installed outside default PATH + + +##### support for git installed outside default PATH The normal solution is to add to the system default PATH somehow, either by munging `/etc/profile` or by enabling `PermitUserEnvironment` in @@ -476,7 +488,9 @@ the full PATH in the rc file, like so: $ENV{PATH} = "/home/sitaram/bin:$ENV{PATH}"; -#### "personal" branches + + +##### "personal" branches "personal" branches are great for corporate environments, where unauthenticated pull/clone is a no-no. Since a dev workstation cannot do @@ -498,7 +512,9 @@ important thing is that the "branch" name should contain `/USER/` (including the slashes). At runtime this will match whoever is the current user. Access is still determined by the right hand side of course. -#### custom hooks and custom git config + + +##### custom hooks and custom git config You can specify hooks that you want to propagate to all repos, as well as per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and @@ -506,13 +522,17 @@ per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and -### helping with gitweb + + +#### helping with gitweb Although gitweb is a completely separate program, gitolite can do quite a lot to help you manage gitweb access as well; once the initial setup is complete, you can do it all from within the gitolite config file! -#### easier to specify gitweb "description" and gitweb/daemon access + + +##### easier to specify gitweb "description" and gitweb/daemon access To enable access to a repo via gitweb *and* create a "description" for it to show up on the webpage, just add a line like this, anywhere in the config @@ -561,7 +581,9 @@ Here's an example, using really short reponames because I'm lazy: -#### easier to link gitweb authorisation with gitolite + + +##### easier to link gitweb authorisation with gitolite Over and above whether a repo is even *shown* by gitweb, you may want to further restrict people, allowing them to view *only* those repos for which @@ -595,18 +617,26 @@ Gitweb allows you to specify a subroutine to decide on access. We use that feature and tie it to gitolite. Configuration example can be found in `contrib/gitweb/`. -### advanced features + -#### repos named with wildcards +#### advanced features + + + +##### repos named with wildcards Please see `doc/4-wildcard-repositories.mkd` for all the details. -#### admin defined commands + + +##### admin defined commands This requires the wildcards feature to be enabled, but is then an extremely powerful feature. See `doc/admin-defined-commands.mkd`. -#### access control for external commands + + +##### access control for external commands Gitolite now has a mechanism for allowing access control for arbitrary external commands, as long as they are invoked via ssh and present a @@ -630,7 +660,9 @@ Commands implemented so far are: -##### svnserve + + +###### svnserve If you are transitioning from SVN to gitolite, and have a lot of users using public-key authentication with SVN, this feature may be useful to you. Once @@ -640,9 +672,13 @@ system. Assuming you installed gitolite to the same user as the one you used for SVN, SVN connectivity will be retained, and users will be able to use both SVN and git using the same SSH configuration. -## design choices + -### keeping the parser and the access control separate +### design choices + + + +#### keeping the parser and the access control separate There are two programs concerned with access control: diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 9837bfc..4393e5a 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -19,34 +19,42 @@ workarounds I may not have the time to code it right away. In this document: - * rc file setting required - * wildcard repos - * wildcard repos with creator name in them - * wildcard repos without creator name in them - * side-note: line-anchored regexes - * contrast with refexes - * handing out rights to wildcard-matched repos - * setting a gitweb description for a wildcard-matched repo - * reporting - * other issues and discussion - * how it actually works + * rc file setting required + * Wildcard repos + * Wildcard repos with creator name in them + * Wildcard repos without creator name in them + * Side-note: Line-anchored regexes + * Contrast with refexes + * Handing out rights to wildcard-matched repos + * setting a gitweb description for a wildcard-matched repo + * reporting + * other issues and discussion + * how it actually works + +---- This document is mostly "by example". ---- + + ### rc file setting required This feature requires that you set `$GL_WILDREPOS` to "1" in `~/.gitolite.rc` on the server. Please search for that variable and see comments around it in `conf/example.gitolite.rc` for more information on this. + + ### Wildcard repos Which of these alternatives you choose depends on your needs, and the social aspects of your environment. The first one is a little more rigid, making it harder to make mistakes, and the second is more flexible and trusting. + + #### Wildcard repos with creator name in them Here's an example snippet: @@ -71,6 +79,8 @@ new repo, as user "u4" (a student): Notice the *two* empty repo inits, and the order in which they occur ;-) + + #### Wildcard repos without creator name in them Here's how the same example would look if you did not want the CREATOR's name @@ -96,6 +106,8 @@ and have a TA create the repos in advance. In either case, they could then use the `setperms` feature to specify which users are "READERS" and which are "WRITERS". See later for details. + + ### Side-note: Line-anchored regexes A regex like @@ -111,6 +123,8 @@ But you may be surprised to find that it does not match even `^assignments/S[0-9]+/A[0-9]+$` -- notice the line beginning and ending metacharacters. + + #### Contrast with refexes Just for interest, note that this is in contrast to the refexes for the normal @@ -121,6 +135,8 @@ if no one will actually push such a branch! You can anchor both sides if you really care, by using `master$` instead of `master`, but that is *not* the default for refexes. + + ### Handing out rights to wildcard-matched repos In the examples above, we saw two special "user" names: READERS and WRITERS. @@ -166,11 +182,15 @@ The following points are important: * whoever you specify as "R" will match the special user READERS. "RW" will match WRITERS. + + ### setting a gitweb description for a wildcard-matched repo Similar to the getperm/setperm commands, there are the getdesc/setdesc commands, thanks to Teemu. + + ### reporting Remember the cool stuff you see when you just do `ssh git@server` (grep for @@ -195,6 +215,8 @@ Push the config, then try ssh gitolite expand + + ### other issues and discussion * *what if the repo name being pushed matches more than one pattern*? @@ -215,6 +237,8 @@ Push the config, then try of the time, it won't be difficult; the fixed prefix will usually be different anyway so there won't be overlaps. + + ### how it actually works This section tells you what is happening inside gitolite so you can understand diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index e3403ca..79864c0 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -2,6 +2,19 @@ [Thanks to jeromeag for forcing me to think through this...] +---- + +In this document: + + * lots of repos, lots of users + * splitting up the set of repos into groups + * delegating ownership of groups of repos + * security/philosophy note + +---- + + + ### lots of repos, lots of users Gitolite tries to make it easy to manage access to lots of users and repos, @@ -34,6 +47,8 @@ access control rules for a set of repos they have been given authority for. It's easier to show how it all works with an example instead of long descriptions. + + ### splitting up the set of repos into groups To start with, recall that gitolite allows you to specify **groups** (of users @@ -48,6 +63,8 @@ or repos, same syntax). So the basic idea is that the main config file # any other config as usual, including access control lines for any of the # above projects or groups + + ### delegating ownership of groups of repos Once the repos are grouped, give each person charge of one or more groups. @@ -100,7 +117,9 @@ to the bottom of the main file. ---- -### Security/Philosophy note + + +### security/philosophy note The delegation feature is meant only for access control rules, not pubkeys. Adding/removing pubkeys is a much more significant event than changing branch diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 83da139..697ed76 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -2,38 +2,44 @@ In this document: - * the most common problems that an admin will see - * basic ssh troubleshooting - * passphrases versus passwords - * ssh-agent problems - * basic ssh troubleshooting for the main admin - * basic ssh troubleshooting for a normal user - * details - * files on the server - * files on client - * why two keys on client - * more complex ssh setups - * two gitolite servers to manage? - * giving shell access to gitolite users + * the most common problems that an admin will see + * basic ssh troubleshooting + * passphrases versus passwords + * ssh-agent problems + * basic ssh troubleshooting for the main admin + * basic ssh troubleshooting for a normal user + * details + * files on the server + * files on client + * why two keys on client + * some other tips and tricks + * two gitolite servers to manage? + * giving shell access to gitolite users + * losing your admin key + * simulating ssh-copy-id ---- This document should help you troubleshoot ssh-related problems in accessing gitolite *after* the install has completed successfully. -In addition, I **strongly** recommend reading [this document][glb] -- it's a -very detailed look at how gitolite uses ssh's features on the server side. -Most people don't know ssh as well as they *think* they do; even if you don't -have any problems right now, it's worth skimming over. +In addition, I **strongly** recommend reading +[doc/9-gitolite-and-ssh.mkd][doc9gas], which is a very detailed look at how +gitolite uses ssh's features on the server side. Most people don't know ssh +as well as they *think* they do; even if you don't have any problems right +now, it's worth skimming over. In addition to both these documents, there's now a program called `sshkeys-lint` that you can run on your client. Run it without arguments to get help on how to run it and what inputs it needs. + + ### the most common problems that an admin will see Ironically, these problems **only** happen to the person who installed -gitolite using easy-install, and has **utterly failed** to read/heed the +gitolite using easy-install (the "from-client" method in +[doc/0-INSTALL.mkd][doc0]), and has **utterly failed** to read/heed the message that shows up at the end of running that command. This is because only the admin has *two* ssh keys to the server (see "basic ssh troubleshooting for the main admin" section below for more on this). @@ -44,7 +50,9 @@ completely**: * you get `fatal: 'reponame' does not appear to be a git repository`, and yet you are sure 'reponame' exists, you haven't mis-spelled it, etc. - * you are able to clone repositories but are unable to push changes back. + * you are able to clone repositories but are unable to push changes back + (the error complains about the `GL_RC` environment variable not being set, + and the `hooks/update` failing in some way). Let us recap the message that appears on a successful run of the "easy-install" program; it looks something like this (with suitable values substituted for @@ -77,9 +85,7 @@ the repo and clones ok. But when you push, gitolite's **update hook** kicks in, and fails to run because you some of the environment variables it is expecting are not present. ----- - - + ### basic ssh troubleshooting @@ -92,6 +98,8 @@ username* of the admin. Unless specifically mentioned, all these commands are run on the user's or admin's workstation, not on the server. + + #### passphrases versus passwords When you create an ssh keypair, you have the option of protecting it with a @@ -111,6 +119,8 @@ The second is to use `ssh-agent` (or `keychain`, which in turn uses `ssh-agent`) or something like that to manage your keys. Other than the next section, further discussion of this is out of scope of this document. + + #### ssh-agent problems 1. Run `ssh-add -l`. If this responds with either "The agent has no @@ -135,6 +145,8 @@ all, ssh will only use those keys. Even if you explicitly specify an unlisted key using `ssh -i` or an `identityfile` directive in the config file, it won't use it. + + #### basic ssh troubleshooting for the main admin You're the "main admin" if you're trying to access gitolite from the same @@ -203,6 +215,8 @@ from scratch: That's a long sequence but it should work. + + #### basic ssh troubleshooting for a normal user For a normal user, life is much simpler. They should have only one pubkey, @@ -222,10 +236,14 @@ the admin repo, it won't work. For reasons why, see below. + + ### details Here's how it all hangs together. + + #### files on the server * the authkeys file; this contains one line containing the pubkey of each @@ -256,6 +274,8 @@ Here's how it all hangs together. argument `sitaram`. This is how gitolite is invoked, (and is told the user logging in is "sitaram"). + + #### files on client * default keypair; used to get shell access to servers. You would have @@ -326,8 +346,13 @@ Here's how it all hangs together. + + #### why two keys on client +[This section is only applicable to installs done using the "from-client" +method; see [doc/0-INSTALL.mkd][doc0] for details]. + Why do I (the admin) need two **different** keypairs? There are two types of access the admin will make to the server: a normal @@ -390,10 +415,11 @@ That should do it. -### more complex ssh setups + -What do you need to know in order to create more complex ssh setups (for -instance if you have *two* gitolite servers you are administering)? +### some other tips and tricks + + #### two gitolite servers to manage? @@ -415,9 +441,9 @@ instance if you have *two* gitolite servers you are administering)? * now access one server's repos as `gitolite:reponame.git` and the other server's repos as `gitolite2:reponame.git`. - + -### giving shell access to gitolite users +#### giving shell access to gitolite users We've managed (thanks to an idea from Jesse Keating) to make it possible for a single key to allow both gitolite access *and* shell access. @@ -442,3 +468,41 @@ access would not manage to get himself shell access. Giving someone shell access requires that you should have shell access in the first place, so the simplest way is to enable it from the server side only. + + + +#### losing your admin key + +If you lost the admin key, and need to re-establish ownership of the +gitolite-admin repository with a fresh key, take a look at the +`src/gl-emergency-addkey` program. You will need shell access to the server +of course. The top of the script has useful information on how to use it and +what it needs. + + + +#### simulating ssh-copy-id + +don't have `ssh-copy-id`? This is broadly what that command does, if you want +to replicate it manually. The input is your pubkey, typically +`~/.ssh/id_rsa.pub` from your client/workstation. + + * it copies it to the server as some file + + * it appends that file to `~/.ssh/authorized_keys` on the server + (creating it if it doesn't already exist) + + * it then makes sure that all these files/directories have go-w perms + set (assuming user is "git"): + + /home/git/.ssh/authorized_keys + /home/git/.ssh + /home/git + +[Actually, `sshd` requires that even directories *above* `~` (`/`, `/home`, +typically) also must be `go-w`, but that needs root. And typically +they're already set that way anyway. (Or if they're not, you've got +bigger problems than gitolite install not working!)] + +[doc0]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd +[doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-gitolite-and-ssh.mkd diff --git a/doc/7-install-transcript.mkd b/doc/7-install-transcript.mkd index 0127d46..3b5c6c7 100644 --- a/doc/7-install-transcript.mkd +++ b/doc/7-install-transcript.mkd @@ -15,20 +15,21 @@ Please note that this entire transcript can be summarised as: ...and only that last step is actually gitolite. In fact, the bulk of the transcript is **non**-gitolite stuff :) ----- +**Please also note that this method will setup everything on the server, but +you have to run it on your workstation, NOT on the server!** -### (cleaning out a botched install if needed) +In this document: -When people have trouble installing gitolite, they often try to change a bunch -of things manually on the server. This usually makes things worse ;-) so if -you're reading this document as a follow-up to a failed install, please -**first** clean out the botched install (instructions [here][appB]) and -**then** continue with this document. - -[appB]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#uninstall + * create userids on server and client (optional) + * get pubkey access from client to server + * get gitolite source + * install gitolite + * examine what you have ---- + + ### create userids on server and client (optional) Client side: add user, give him a password @@ -74,6 +75,8 @@ because I'm not showing the actual "vi" session): ---- + + ### get pubkey access from client to server This involves creating a keypair for yourself (using `ssh-keygen`), and @@ -123,6 +126,8 @@ Double check to make sure you can log on to `git@server` without a password: ---- + + ### get gitolite source sita@sita-lt:~ $ git clone git://github.com/sitaramc/gitolite gitolite-source @@ -133,6 +138,8 @@ Double check to make sure you can log on to `git@server` without a password: Receiving objects: 100% (1157/1157), 270.08 KiB | 61 KiB/s, done. Resolving deltas: 100% (756/756), done. + + ### install gitolite Note that gitolite is installed from the *client*. The `easy-install` script @@ -145,7 +152,6 @@ install sequence**. Run it without any arguments to see a usage message. Run it without the `-q` to get a more verbose, pause-at-every-step, install mode that allows you to change the defaults etc. - sita@sita-lt:src $ ./gl-easy-install -q git server sitaram you are upgrading (or installing first-time) to v0.95-38-gb0ce84d setting up keypair... @@ -203,6 +209,8 @@ install mode that allows you to change the defaults etc. ---- + + ### examine what you have sita@sita-lt:src $ cd ~/gitolite-admin/ diff --git a/doc/9-gitolite-and-ssh.mkd b/doc/9-gitolite-and-ssh.mkd new file mode 100644 index 0000000..85db02c --- /dev/null +++ b/doc/9-gitolite-and-ssh.mkd @@ -0,0 +1,155 @@ +# how gitolite uses ssh + +Here's a secret: ***gitolite uses far more ssh magic than git magic***! + +Most people didn't realise this, and even if they did they didn't know ssh +well enough to help themselves. If you don't understand how ssh public key +authentication works, or how the `~/.ssh/authorized_keys` file can be used to +restrict users, etc., you will have endless amounts of trouble getting +gitolite to work, because you'll be attacking the wrong problem. + +So please please please understand this before tearing your hair out and +blaming ***git/gitolite*** for whatever is going wrong with your setup :-) + +In this document: + + * ssh basics + * how does gitolite use all this ssh magic? + * restricting shell access/distinguishing one user from another + * restricting branch level actions + +---- + + + +### ssh basics + +Let's start with some basics, focusing *only* on the pieces relevant to +`gitolite`. If this is not detailed enough, please use google and learn more +from somewhere, or maybe buy the OReilly ssh book. + + * You can login to an ssh server by typing a password, but ssh can also use + ***public-private keys*** (also called "key pairs") for authentication. + `gitolite` *requires* you to use this mechanism for your users -- they + cannot log in using passwords. Hopefully by the time you finish reading + this document you will understand why :-) + + The way you set this up is you generate a key pair on your workstation, + and give the server the public key. (I need not add that the "private" + key must be, well, kept *private*!) + + * **generating a key pair on your workstation** is done by running the + command `ssh-keygen -t rsa`. This produces two files in `~/.ssh`. One is + `id_rsa`; this is the **private** key -- ***never*** let it out of your + machine. The other is `id_rsa.pub`, which is the corresponding public + key. This public key is usually just one long line of text. + + * on Windows machines with msysgit installed, you should do this from + within a "git bash" window. The command will report the full path where + the files have been written; make a note of this, and use those files in + any of the description that follows + + * **adding your public key to the server**'s `~/.ssh/authorized_keys` + file is how ssh uses pubkeys to authenticate users. Let's say + sita@work.station is trying to log in as git@serv.er. What you have to do + is take the `~/.ssh/id_rsa.pub` file for user sita on work.station and + append its contents (remember it's only one line) to + `~/.ssh/authorized_keys` for user git on serv.er. + + The `authorized_keys` file can have multiple public keys (from many + different people) added to it so any of them can log in to git@serv.er. + + In the normal case (not gitolite, but your normal everyday shell access), + there's a command that does this, `ssh-copy-id`, which also fixes up + permissions etc., as needed, since sshd is a little picky about allowing + pubkey access if permissions on the server are loose. Or you can do it + manually, as long as you know what you're doing and you're careful not to + erase or overwrite the existing contents of `~/.ssh/authorized_keys` on + the server! + + But in the gitolite case, it's different; we'll get to that in a minute. + + * **troubleshooting pubkey authentication failures**: if you are unable to + get ssh access to the server after doing all this, you'll have to look + in `/var/log/secure` or `/var/log/auth.log` or some such file on the + server to see what specific error `sshd` is complaining about. + + * **restricting users to specific commands** is very important for gitolite. + If you read `man sshd` and look for `authorized_keys file format`, you'll + see a lot of options you can add to the public key line, which restrict + the incoming user in various ways. In particular, note the `command=` + option, which means "regardless of what the incoming user is asking to do, + forcibly run this command instead". + + Also note that when there are many public keys (i.e., lines) in the + `authorized_keys` file, each line can have a *different* set of options + and `command=` values. + + **This is the backbone of what makes gitolite work; please make sure you + understand this** + + + +### how does gitolite use all this ssh magic? + +These are two different questions you ought to be having by now: + + * how does it distinguish between me and someone else, since we're all + logging in as the same remote user "git" + * how does it restrict what I can do within a repository + + + +#### restricting shell access/distinguishing one user from another + +The answer to the first question is the `command=` we talked about before. If +you look in the `authorized_keys` file, you'll see entries like this (I chopped +off the ends of course; they're pretty long lines): + + command="[path]/gl-auth-command sitaram",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA18S2t... + command="[path]/gl-auth-command usertwo",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArXtCT... + +First, it finds out which of the public keys in this file match the incoming +login. That's crypto stuff, and I won't go into it. Once the match has been +found, it will run the command given on that line; e.g., if I logged in, it +would run `[path]/gl-auth-command sitaram`. So the first thing to note is +that such users do not get "shell access", which is good! + +Before running the command, however, sshd sets up an environment variable +called `SSH_ORIGINAL_COMMAND` which contains the actual git command that your +workstation sent out. This is the command that *would have run* if you did +not have the `command=` part in the authorised keys file. + +When `gl-auth-command` gets control, it looks at the first argument +("sitaram", "usertwo", etc) to determine who you are. It then looks at the +`SSH_ORIGINAL_COMMAND` variable to find out which repository you want to +access, and whether you're reading or writing. + +Now is has user, repository, and access requested (read/write), gitolite looks +at its config file, and either allows or rejects the request. + +But this cannot differentiate between different branches within a repo; that +has to be done separately. + + + +#### restricting branch level actions + +[If you look inside the git source tree, there's a file among the "howto"s in +there called `update-hook-example.txt`, which was the inspiration for this +part of gitolite.] + +Git allows you to specify many "hooks", which get control as various events +happen -- see `git help hooks` for details. One of those hooks is the +`update` hook, which, if it is present, is invoked just before a branch or a +tag is about to be updated. The hook is passed the name of the branch or tag, +the old SHA1 value, and the new SHA1 value, as arguments. Hooks that are +called *before* an action happens are allowed to prevent that action from +happening y returning an error code. + +When gitolite is told to create a new repository (by the admin), it installs +a special update hook. This hook takes all the information presented, looks +at the config file, and decides to allow or reject the update. + +And that's basically it. + diff --git a/doc/9-packaging.mkd b/doc/9-packaging.mkd new file mode 100644 index 0000000..8b51d72 --- /dev/null +++ b/doc/9-packaging.mkd @@ -0,0 +1,56 @@ +# packaging gitolite + +Here's how you'd package gitolite. In the following description, location "X" +can be, say, `/usr/share/gitolite/conf` or some such, and similarly location +"Y" can be perhaps `/usr/share/gitolite/hooks`. It's upto your distro +policies where they are. + +**Step 1**: Clone the gitolite repo and run the make command inside the clone + + git clone git://github.com/sitaramc/gitolite.git + cd gitolite + make pu.tar # or "make master.tar" or "make v1.2.tar" etc + +Then you explode the tar file in some temporary location. + +*Alternatively, you can `git checkout` the tag or branch you want, and run +this command in the clone directly*: + + git describe --tags --long > conf/VERSION + +**Step 2**: Now make the following changes (no trailing slashes in the +location values please): + + * `src/gl-setup` should have the following line: + + GL_PACKAGE_CONF="X" + + * `conf/example.gitolite.rc` should have the following lines: + + $GL_PACKAGE_CONF="X"; + $GL_PACKAGE_HOOKS="Y"; + + * delete `src/gl-easy-install`; that script is meant for a totally different + mode of installation and does *not* play well in this mode :-) + +**Step 3**: Move (or arrange to move) the files to their proper locations as +given below: + + * everything in "src" goes somewhere on the PATH + * everything in "conf" goes to location "X" + * everything in "hooks" goes to location "Y" + +**Step 4**: There is no step 4. Unless you count telling your users to run +`gl-setup` as a step :) + +On the initial install (urpmi, yum install, or apt-get install), you could +also choose to setup a userid called "gitolite", and run "gl-setup" as that +user; however I do not know how you would come up with the initial pubkey that +is needed. Anyway, the point is that the "gitolite" user is no more special +than any other in terms of hosting gitolite. Any user can host gitolite on +his userid by just running "gl-setup". + +When you upgrade, just overwrite all the files; it'll all just work. In fact, +other than the initial "gl-setup" run, the only time a gitolite hosting user +has to actually do anything is to edit their own `~/.gitolite.rc` file if they +want to enable or disable specific features. diff --git a/doc/9-uninstall.mkd b/doc/9-uninstall.mkd new file mode 100644 index 0000000..9cbe829 --- /dev/null +++ b/doc/9-uninstall.mkd @@ -0,0 +1,67 @@ +# uninstalling gitolite + +Sometimes you might find gitolite is overkill -- you have only one user +(yourself) pushing maybe. Or maybe gitolite is just not enough -- you want a +web-based front end that users can use to manage their keys themselves, etc., +in which case you'd probably switch to [github][g1], [girocco][g2], +[indefero][g3] or [gitorious][g4]. [Gerrit][g5] is quite nice too, if you +want collaborative code review there's nothing like it. Either way, you'd +like to uninstall gitolite. + +[g1]: http://github.com +[g2]: http://repo.or.cz/w/girocco.git +[g3]: http://www.indefero.net/ +[g4]: http://gitorious.com/ +[g5]: http://code.google.com/p/gerrit/ + +Uninstalling gitolite is fairly easy, although it is manual. (We'll assume +`$REPO_BASE` in the rc file was left at its default of `~/repositories`; if +not, adjust accordingly): + +**server side tasks** + + * edit `~/.ssh/authorized_keys` and delete the `# gitolite start` and `# + gitolite end` markers and all the lines between them. This will prevent + any of your users from attempting a push while you are doing this. + + If you are the only user, and/or *need* one or more of those keys to + continue to access this account (like if one of them is your laptop or + your home desktop etc.) then instead of deleting the line you can just + delete everything upto but not including the words "ssh-rsa" or "ssh-dss". + + * Now remove (or move aside or rename to something else if you're paranoid) + the following files and directories. + + ~/.gitolite + ~/.gitolite.rc + ~/repositories/gitolite-admin.git + + * You can remove all of `~/repositories` if you have not really started + using gitolite properly yet; that's your choice. + + If you *do* need to preserve the other repos and continue to use them, + remove all the `update` hooks that git installs on each repository. The + easiest way is: + + find ~/repositories -wholename "*.git/hooks/update" | xargs rm -f + + but you can do it manually if you want to be careful. + +**client side tasks** + + * Any remote users that still have access must update their clone's remote + URLs (edit `.git/config` in the repo) to prefix `repositories/` before the + actual path used, in order for the remote to still work. This is because + you'll now be accessing it through plain ssh, which means you have to give + it the full path. + + * Finally, you as the gitolite admin will probably have a host stanza for + "gitolite" in your *client*'s `~/.ssh/config`. Find and delete lines that + look like this: + + host gitolite + user git + hostname your.server + port 22 + identityfile ~/.ssh/your-gitolite-admin-username + diff --git a/doc/TODO b/doc/TODO deleted file mode 100644 index 9c6faef..0000000 --- a/doc/TODO +++ /dev/null @@ -1,3 +0,0 @@ - * make a proper test suite - - * change the "rc" file to use "gitconfig" instead... diff --git a/doc/admin-defined-commands.mkd b/doc/admin-defined-commands.mkd index a1d1b89..969f420 100644 --- a/doc/admin-defined-commands.mkd +++ b/doc/admin-defined-commands.mkd @@ -11,16 +11,18 @@ There may be other such **WARNING** sections below. **Read all of them**. In this document: - * background - * setting it up - * anatomy of a command - * example uses and sample commands in contrib - * fork - * rmrepo - * (bonus) restricted admin + * background + * setting it up + * anatomy of a command + * example uses and sample commands in contrib + * fork + * rmrepo + * (bonus) restricted admin ---- + + ### background Gitolite was named to be short for "gitosis-lite". Someone now wants to turn @@ -59,6 +61,8 @@ What we want now is more than that, as you'll see in the examples below. And I'm sure if you think of more uses you'll send them to me as "contrib" entries, right? + + ### setting it up This can only be setup by someone who has shell access to the server. Edit @@ -77,6 +81,8 @@ to inadvertently *hide* some of the "official" commands (like "info", executable files with those names in this directory. So don't do that -- you have been warned!** + + ### anatomy of a command You can basically do whatever you want in such a command -- go wild! It's @@ -130,8 +136,12 @@ convenient. See any of the other samples for how to use it. If you don't like this, roll your own. If you don't like bash, do the eqvt in your language of choice. + + ### example uses and sample commands in contrib + + #### fork A user would use the fork command like this: @@ -165,6 +175,8 @@ So now we have an actual command to just create a repo and do nothing else: `ssh git@server git-init \'reponame\'`. [Yes; those single quotes are required. Deal with it.] + + #### rmrepo This is one thing that you really could not do before this setup was created. @@ -175,6 +187,8 @@ Use it like this: The script checks to make sure that the repo being deleted was *created* by the user invoking it. + + #### (bonus) restricted admin It's rather important to me (and presumably others in the "corporate" world) diff --git a/doc/big-config.mkd b/doc/big-config.mkd index 5965aca..5531167 100644 --- a/doc/big-config.mkd +++ b/doc/big-config.mkd @@ -2,11 +2,13 @@ In this document: - * when/why do we need it? - * how do we use it? - * summary of settings in RC file - * what are the downsides? - * (extra coolness) usergroups and LDAP/similar tools + * when/why do we need it? + * how do we use it? + * summary of settings in RC file + * what are the downsides? + * (extra coolness) usergroups and LDAP/similar tools + + ### when/why do we need it? @@ -93,6 +95,8 @@ Phew! You can imagine what that does when you have 10,000 users and 10,000 repos. Let's just say it's not pretty :) + + ### how do we use it? Now, if you had all those 10,000 users and repos explicitly listed (no @@ -138,6 +142,8 @@ configuration, the compiled file looks like this: That's a lot smaller, and allows orders of magintude more repos and groups to be supported. + + ### summary of settings in RC file The default RC file contains the following lines: @@ -156,6 +162,8 @@ you push the gitolite-admin repo with changes. [gw]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gitweb + + ### what are the downsides? There is one minor issue. @@ -168,6 +176,8 @@ subset of the allowed @fragname, which would work normally, do not work now). (If you didn't understand all that, you're probably not using delegation, so feel free to ignore it!) + + ### (extra coolness) usergroups and LDAP/similar tools [Please NOTE: this is all about *user* groups, not *repo* groups] diff --git a/doc/progit-article.mkd b/doc/progit-article.mkd index 916faf6..8d7fa4f 100644 --- a/doc/progit-article.mkd +++ b/doc/progit-article.mkd @@ -2,7 +2,7 @@ Git has started to become very popular in corporate environments, which tend to have some additional requirements in terms of access control. Gitolite was created to help with those requirements. -Gitolite allows you to specify permissions not just by repository (like Gitosis does), but also by branch or tag names within each repository. That is, you can specify that certain people (or groups of people) can only push certain "refs" (branches or tags) but not others. +Gitolite allows you to specify permissions not just by repository, but also by branch or tag names within each repository. That is, you can specify that certain people (or groups of people) can only push certain "refs" (branches or tags) but not others. ### Installing ### @@ -25,7 +25,7 @@ Next, you clone Gitolite from the project's main site and run the "easy install" $ cd gitolite/src $ ./gl-easy-install -q gitolite gitserver sitaram -And you're done! Gitolite has now been installed on the server, and you now have a brand new repository called `gitolite-admin` in the home directory of your workstation. You administer your gitolite setup by making changes to this repository and pushing (just like Gitosis). +And you're done! Gitolite has now been installed on the server, and you now have a brand new repository called `gitolite-admin` in the home directory of your workstation. You administer your gitolite setup by making changes to this repository and pushing. [By the way, *upgrading* gitolite is also done the same way. Also, if you're interested, run the script without any arguments to get a usage message.] @@ -57,7 +57,7 @@ So once the install is done, you switch to the `gitolite-admin` repository (plac Notice that "sitaram" (the last argument in the `gl-easy-install` command you gave earlier) has read-write permissions on the `gitolite-admin` repository as well as a public key file of the same name. -The config file syntax for Gitolite is *quite* different from Gitosis. Again, this is liberally documented in `conf/example.conf`, so we'll only mention some highlights here. +The config file syntax for gitolite is liberally documented in `conf/example.conf`, so we'll only mention some highlights here. You can group users or repos for convenience. The group names are just like macros; when defining them, it doesn't even matter whether they are projects or users; that distinction is only made when you *use* the "macro". @@ -90,7 +90,7 @@ That rule will just get added to the ruleset for the `gitolite` repository. At this point you might be wondering how the access control rules are actually applied, so let's go over that briefly. -There are two levels of access control in gitolite. The first is at the repository level; if you have read (or write) access to *any* ref in the repository, then you have read (or write) access to the repository. This is the only access control that Gitosis had. +There are two levels of access control in gitolite. The first is at the repository level; if you have read (or write) access to *any* ref in the repository, then you have read (or write) access to the repository. The second level, applicable only to "write" access, is by branch or tag within a repository. The username, the access being attempted (`W` or `+`), and the refname being updated are known. The access rules are checked in order of appearance in the config file, looking for a match for this combination (but remember that the refname is regex-matched, not merely string-matched). If a match is found, the push succeeds. A fallthrough results in access being denied. From 41bec9f25f2ed6b77a9ac8c18002f8eaa2e9a79b Mon Sep 17 00:00:00 2001 From: Matt Perzel Date: Sat, 22 May 2010 17:27:23 -0700 Subject: [PATCH 374/850] Added host_nickname parameter to gl-easy-install --- src/gl-easy-install | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index fdb8776..55d08ff 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -103,12 +103,15 @@ Usage: $0 [-q] user host [port] admin_name # install - "admin_name" is *your* name as it should appear in the eventual gitolite config file. For upgrades (ie., gitolite is already installed on the server), this argument is not needed, and will be *ignored* if provided. + - "host_nickname" is a nickname that can be given to a particular + installation of gitolite. This allows for multiple servers to easily be + administered from this machine; optional, defaults to gitolite -Example usage: $0 git my.git.server sitaram +Example usage: $0 git my.git.server sitaram gitolite_server_1 Notes: - - "user" and "admin_name" must be simple names -- no special characters etc - please (only alphanumerics, dot, hyphen, underscore) + - "user","admin_name" and "host_nickname" must be simple names -- no special + characters etc please (only alphanumerics, dot, hyphen, underscore) - traditionally, the "user" is "git", but it can be anything you want - "admin_name" should be your name, for clarity, or whoever will be the gitolite admin @@ -152,20 +155,33 @@ basic_sanity() { host=$2 port=22 admin_name=$3 + host_nickname=$4 + if [ -z $4 ] + then + host_nickname="gitolite" + fi + # but if the 3rd arg is a number, that's a port number, and the 4th arg is # the admin_name if echo $3 | perl -lne 'exit 1 unless /^[0-9]+$/' then port=$3 admin_name=$4 + host_nickname=$5 + if [ -z $5 ] + then + host_nickname=gitolite + fi fi echo $user | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' || die "user '$user' invalid" [[ "$user" == "root" ]] && die I refuse to install to root echo $admin_name | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' || - die "admin_name '$admin_name' invalid" - + die "admin_name '$admin_name' invalid" + echo $host_nickname | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' || + die "host nickname '$host_nickname' invalid" + # MANUAL: make sure you're in the gitolite directory, at the top level. # The following files should all be visible: @@ -255,13 +271,14 @@ setup_local_ssh() { # port 22 # identityfile ~/.ssh/sitaram - echo "host gitolite + + echo "host $host_nickname user $user hostname $host port $port identityfile ~/.ssh/$admin_name" > $tmpgli/.gl-stanza - if grep 'host *gitolite' "$HOME/.ssh/config" &>/dev/null + if grep "host *$host_nickname" "$HOME/.ssh/config" &>/dev/null then prompt "found gitolite para in ~/.ssh/config; assuming it is correct..." "$v_found_para" else @@ -569,7 +586,11 @@ done! IMPORTANT NOTE -- PLEASE READ!!! *Your* URL for cloning any repo on this server will be - gitolite:reponame.git + \$host_nickname:reponame.git + + Note: If you are upgrading and you set a host nickname during initial + setup, please use that host nickname instead of \"gitolite\" + above. *Other* users you set up will have to use \$user@\$host:reponame.git From c013dbf8f0cc45d7915b528a303ac2b20e04d5ef Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 May 2010 09:07:48 +0530 Subject: [PATCH 375/850] (minor fixups) --- src/gl-easy-install | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 55d08ff..3100f18 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -103,15 +103,17 @@ Usage: $0 [-q] user host [port] admin_name # install - "admin_name" is *your* name as it should appear in the eventual gitolite config file. For upgrades (ie., gitolite is already installed on the server), this argument is not needed, and will be *ignored* if provided. - - "host_nickname" is a nickname that can be given to a particular - installation of gitolite. This allows for multiple servers to easily be - administered from this machine; optional, defaults to gitolite -Example usage: $0 git my.git.server sitaram gitolite_server_1 + - (optional) "host_nickname" is a nickname that can be given to a particular + installation of gitolite. This allows for multiple servers to easily be + administered from this machine. Defaults to "gitolite" + +Example usage: $0 git my.git.server sitaram [gitolite_server_1] Notes: - - "user","admin_name" and "host_nickname" must be simple names -- no special - characters etc please (only alphanumerics, dot, hyphen, underscore) + - "user","admin_name" and "host_nickname" must be simple names -- no + special characters etc please (only alphanumerics, dot, hyphen, + underscore) - traditionally, the "user" is "git", but it can be anything you want - "admin_name" should be your name, for clarity, or whoever will be the gitolite admin @@ -178,10 +180,10 @@ basic_sanity() { die "user '$user' invalid" [[ "$user" == "root" ]] && die I refuse to install to root echo $admin_name | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' || - die "admin_name '$admin_name' invalid" + die "admin_name '$admin_name' invalid" echo $host_nickname | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' || die "host nickname '$host_nickname' invalid" - + # MANUAL: make sure you're in the gitolite directory, at the top level. # The following files should all be visible: @@ -271,7 +273,6 @@ setup_local_ssh() { # port 22 # identityfile ~/.ssh/sitaram - echo "host $host_nickname user $user hostname $host @@ -586,10 +587,10 @@ done! IMPORTANT NOTE -- PLEASE READ!!! *Your* URL for cloning any repo on this server will be - \$host_nickname:reponame.git + \$host_nickname:reponame.git Note: If you are upgrading and you set a host nickname during initial - setup, please use that host nickname instead of \"gitolite\" + setup, please use that host nickname instead of \"gitolite\" above. *Other* users you set up will have to use From f4d21db590560af11ece24de0599d821343a6916 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 May 2010 09:11:30 +0530 Subject: [PATCH 376/850] easy install: clone even if a non-default host_nickname is used --- src/gl-easy-install | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 3100f18..e8fd294 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -450,12 +450,12 @@ clone_it() { cleanup cd "$HOME" - if [[ -d gitolite-admin ]] + if [[ -d $host_nickname-admin ]] then - echo $HOME/gitolite-admin exists, skipping clone step... + echo $HOME/$host_nickname-admin exists, skipping clone step... else - prompt "cloning gitolite-admin repo..." "$v_cloning" - git clone gitolite:gitolite-admin + prompt "cloning $host_nickname-admin repo..." "$v_cloning" + git clone $host_nickname:gitolite-admin $host_nickname-admin fi # MANUAL: be sure to read the message below; this applies to you too... From 701b1820212f892f8df935c62083613b7f7eb441 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 23 May 2010 12:04:01 +0530 Subject: [PATCH 377/850] document the add_host_nickname branch changes --- doc/0-INSTALL.mkd | 55 +++++++++++++++++++++++++---------- doc/6-ssh-troubleshooting.mkd | 33 ++++----------------- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index c21d03c..754f4d2 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -13,11 +13,13 @@ In this document: * (root method) directly on the server, manually, with root access * (non-root method) directly on the server, manually, without root access * (from-client method) install from the client to the server - * special cases -- multiple gitolite instances - * upgrading - * uninstalling - * cleaning out a botched install - * uninstalling gitolite completely + * special cases -- multiple gitolite servers + * package method and root method + * from-client method + * upgrading + * uninstalling + * cleaning out a botched install + * uninstalling gitolite completely ---- @@ -162,13 +164,16 @@ including *outputs* of the commands concerned. -### special cases -- multiple gitolite instances +### special cases -- multiple gitolite servers -With the first two methods (package or root methods) of installation, it's -trivial to create multiple gitolite instances (say one for each department, on -some mega company-wide server). You can even do this without giving shell -access to the admins. Here's an example with just two "departments", and -their admins Alice and Bob: + + +#### package method and root method + +With the first two methods of installation, it's trivial to create multiple +gitolite instances (say one for each department, on some mega company-wide +server). You can even do this without giving shell access to the admins. +Here's an example with just two "departments", and their admins Alice and Bob: * create userids `webbrowser_repos` and `webserver_repos` * ask Alice and Bob for their pubkeys; copy them to the respective home @@ -185,7 +190,27 @@ to have a command line on the server, so don't give them the passwords if you don't need to -- the pubkey will allow them to be gitolite admins on their domain, and that's quite enough for normal operations. - + + +#### from-client method + +Thanks to Matt Perzel, the easy-install command now takes an optional 4th +parameter, which is the "nickname" of the gitolite server. It gets defined in +`~/.ssh/config`, and if not used it defaults to "gitolite". + +So if you used the following command to install gitolite to 2 different +servers: + + ./src/gl-easy-install -q git my.1st.git.server admin_user1 gitolite_server_1 + ./src/gl-easy-install -q git my.2nd.git.server admin_user1 gitolite_server_2 + +you will find that `~/gitolite_server_1-admin` and `~/gitolite_server_2-admin` +have been created as respective clones. Or you can re-clone elsewhere: + + cd ~/admin1; git clone gitolite_server_1:gitolite-admin.git + cd ~/admin2; git clone gitolite_server_2:gitolite-admin.git + + ### upgrading @@ -197,11 +222,11 @@ arguments! Also, remember that some new features may require additional settings in your `~/.gitolite.rc` file. - + ### uninstalling - + #### cleaning out a botched install @@ -219,7 +244,7 @@ here's how to clean the slate. * remove `~/.gitolite`, `~/.gitolite.rc` and `~/repositories/gitolite-admin.git` - + #### uninstalling gitolite completely diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 697ed76..b996549 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -13,10 +13,9 @@ In this document: * files on client * why two keys on client * some other tips and tricks - * two gitolite servers to manage? - * giving shell access to gitolite users - * losing your admin key - * simulating ssh-copy-id + * giving shell access to gitolite users + * losing your admin key + * simulating ssh-copy-id ---- @@ -421,28 +420,6 @@ That should do it. -#### two gitolite servers to manage? - - * they can have the same key; no harm there (example, sitaram.pub) - - * instead of just one ssh/config para, you now have two (assuming that the - remote user on both machines is called "git"): - - host gitolite - user git - hostname server - identityfile ~/.ssh/sitaram - - host gitolite2 - user git - hostname server2 - identityfile ~/.ssh/sitaram - - * now access one server's repos as `gitolite:reponame.git` and the other - server's repos as `gitolite2:reponame.git`. - - - #### giving shell access to gitolite users We've managed (thanks to an idea from Jesse Keating) to make it possible for a @@ -469,7 +446,7 @@ access would not manage to get himself shell access. Giving someone shell access requires that you should have shell access in the first place, so the simplest way is to enable it from the server side only. - + #### losing your admin key @@ -479,7 +456,7 @@ gitolite-admin repository with a fresh key, take a look at the of course. The top of the script has useful information on how to use it and what it needs. - + #### simulating ssh-copy-id From 798762a0c31d27be74929805a4e728fd222e7a0c Mon Sep 17 00:00:00 2001 From: "martin f. krafft" Date: Thu, 27 May 2010 19:47:06 +0200 Subject: [PATCH 378/850] gitweb: default GL_USER to gitweb if not provided by CGI If CGI.pm does not have a user, this patch causes the gitweb authentication code to assume "gitweb". This allows one to specify ACLs specifically for gitweb, separately from the @all catch-all. To: Sitaram Chamarty Cc: Teemu Matilainen Signed-off-by: martin f. krafft --- contrib/gitweb/gitweb.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/gitweb/gitweb.conf b/contrib/gitweb/gitweb.conf index efe93a3..edf9af4 100644 --- a/contrib/gitweb/gitweb.conf +++ b/contrib/gitweb/gitweb.conf @@ -8,7 +8,7 @@ my $gl_home = "/home/git"; # environment variables needed by gitolite.pm $ENV{GL_RC} = "$gl_home/.gitolite.rc"; -$ENV{GL_USER} = $cgi->remote_user; +$ENV{GL_USER} = $cgi->remote_user || "gitweb"; # variables from the RC file our ($REPO_BASE, $GL_ADMINDIR); From ce2e8b678889d4c683ce166796ae34bb53d6108f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 29 May 2010 19:16:48 +0530 Subject: [PATCH 379/850] (minor) doc/6: mention putty/plink --- doc/6-ssh-troubleshooting.mkd | 49 +++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index b996549..3341b8a 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -8,14 +8,15 @@ In this document: * ssh-agent problems * basic ssh troubleshooting for the main admin * basic ssh troubleshooting for a normal user - * details - * files on the server - * files on client - * why two keys on client - * some other tips and tricks - * giving shell access to gitolite users - * losing your admin key - * simulating ssh-copy-id + * windows issues + * details + * files on the server + * files on client + * why two keys on client + * some other tips and tricks + * giving shell access to gitolite users + * losing your admin key + * simulating ssh-copy-id ---- @@ -233,15 +234,31 @@ you had command line access to the server *before* you were added as a gitolite user. If you send that same key to your gitolite admin to include in the admin repo, it won't work. For reasons why, see below. + + +### windows issues + +On windows, I have only used msysgit, and the openssh that comes with it. +Over time, I have grown to distrust putty/plink due to the number of people +who seem to have trouble when those beasts are involved (I myself have never +used them for any kind of git access). If you have unusual ssh problems that +just don't seem to have any explanation, try removing all traces of +putty/plink, including environment variables, etc., and then try again. + +If you can offer an *authoritative* account of the complications involved, and +how to resolve them and get things working, I'd be happy to credit you and +include it, either directly here if it is short enough or just an external +link, or in contrib/ if it's a longer piece of text. + - + ### details Here's how it all hangs together. - + #### files on the server @@ -273,7 +290,7 @@ Here's how it all hangs together. argument `sitaram`. This is how gitolite is invoked, (and is told the user logging in is "sitaram"). - + #### files on client @@ -345,7 +362,7 @@ Here's how it all hangs together. - + #### why two keys on client @@ -414,11 +431,11 @@ That should do it. - + ### some other tips and tricks - + #### giving shell access to gitolite users @@ -446,7 +463,7 @@ access would not manage to get himself shell access. Giving someone shell access requires that you should have shell access in the first place, so the simplest way is to enable it from the server side only. - + #### losing your admin key @@ -456,7 +473,7 @@ gitolite-admin repository with a fresh key, take a look at the of course. The top of the script has useful information on how to use it and what it needs. - + #### simulating ssh-copy-id From b4c16271308d49593e412287a43341fc161db18e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 31 May 2010 14:12:04 +0530 Subject: [PATCH 380/850] include VERSION details when using gl-system-install from a clone It works fine when you're installing off of a tar file because the Makefile also generates a VERSION file, but when doing from a clone you still need to generate it. (plus minor fix to easy install, in the same area of code) --- src/gl-easy-install | 2 +- src/gl-system-install | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 37cff34..3ea2509 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -215,7 +215,7 @@ version_info() { # MANUAL: if needed, make a note of the version you are upgrading from, and to # record which version is being sent across; we assume it's HEAD - if git rev-parse --is-inside-work-tree 2>/dev/null + if git rev-parse --is-inside-work-tree >/dev/null 2>&1 then git describe --tags --long HEAD 2>/dev/null > conf/VERSION || echo '(unknown)' > conf/VERSION else diff --git a/src/gl-system-install b/src/gl-system-install index 4da7577..b71cde9 100755 --- a/src/gl-system-install +++ b/src/gl-system-install @@ -34,6 +34,14 @@ cp src/* $gl_bin_dir || die "cp src/* to $gl_bin_dir failed" rm $gl_bin_dir/gl-easy-install perl -lpi -e "s(^GL_PACKAGE_CONF=.*)(GL_PACKAGE_CONF=$gl_conf_dir)" $gl_bin_dir/gl-setup +# record which version is being sent across; we assume it's HEAD +if git rev-parse --is-inside-work-tree >/dev/null 2>&1 +then + git describe --tags --long HEAD 2>/dev/null > conf/VERSION || die "git describe failed -- this should not happen!" +else + [[ -f conf/VERSION ]] || echo '(unknown)' > conf/VERSION +fi + cp -R conf/* $gl_conf_dir || die "cp conf/* to $gl_conf_dir failed" perl -lpi \ -e "s(^#\s*\\\$GL_PACKAGE_CONF\s*=.*)(\\\$GL_PACKAGE_CONF = '$gl_conf_dir';)" \ From 5bbd102059c0e4330c455aa07170e58009da62ea Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 31 May 2010 20:39:45 +0530 Subject: [PATCH 381/850] (contrib) how to enable or disable push for maintenance ...for some or all repos (and a minor bug fix in the adc.common-functions file) --- contrib/adc/able | 46 ++++++++++++++++++++++++++++++++ contrib/adc/adc.common-functions | 2 +- doc/admin-defined-commands.mkd | 41 +++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100755 contrib/adc/able diff --git a/contrib/adc/able b/contrib/adc/able new file mode 100755 index 0000000..a776a3a --- /dev/null +++ b/contrib/adc/able @@ -0,0 +1,46 @@ +#!/bin/bash + +# WARNING: USES BASH FEATURES... + +. $(dirname $0)/adc.common-functions + +get_rights_and_owner gitolite-admin +[[ -z $perm_write ]] && die "just *what* are you trying to pull, young man?" + +op=$1 +shift + +locs= +while [[ -n $1 ]] +do + case $1 in + '@all' ) + locs="$locs $HOME" + ;; + * ) + [ -d $loc ] && locs="$locs $GL_REPO_BASE_ABS/$1.git" + [ -d $loc ] || echo "ignoring $1..." + ;; + esac + shift +done + +case $op in + en|enable ) + for l in $locs + do + rm -fv $l/.gitolite.down + done + ;; + dis|disable ) + [[ -t 0 ]] && printf message: + read msg <<<$(cat) + for l in $locs + do + echo $msg > $l/.gitolite.down + done + ;; + * ) + die "argument 1 must be 'en' or 'dis'" + ;; +esac diff --git a/contrib/adc/adc.common-functions b/contrib/adc/adc.common-functions index c67710a..2631ed2 100644 --- a/contrib/adc/adc.common-functions +++ b/contrib/adc/adc.common-functions @@ -6,7 +6,7 @@ die() { echo "$@"; exit 1; } get_rights_and_owner() { local ans - ans=$(perl -I$HOME/.gitolite/src -Mgitolite -e 'cli_repo_rights("'$1'")') + ans=$(perl -I$GL_BINDIR -Mgitolite -e 'cli_repo_rights("'$1'")') # set shell variables as needed owner=${ans#* } diff --git a/doc/admin-defined-commands.mkd b/doc/admin-defined-commands.mkd index 969f420..b5e6f77 100644 --- a/doc/admin-defined-commands.mkd +++ b/doc/admin-defined-commands.mkd @@ -17,7 +17,8 @@ In this document: * example uses and sample commands in contrib * fork * rmrepo - * (bonus) restricted admin + * enable/disable push access temporarily + * (bonus) restricted admin ---- @@ -189,6 +190,44 @@ the user invoking it. +#### enable/disable push access temporarily + +If you want to disable push access to gitolite temporarily (maybe for +maintenance), anyone with write access to the gitolite-admin repo can do this: + + ssh git@server able dis @all # able dis ==> dis able + +To re-enable after the maint work is done: + + ssh git@server able en @all # able en ==> en able + +You can also do this for one or more individual repos; in place of `@all`, +just use a space separated list of reponames (exactly as they would appear in +the config file). Wildcards are not supported; patches welcome ;-) + +**NOTE: This needs a specific secondary update hook**. Creating a secondary +update hook is described in the sections on "custom hooks" and "hook chaining" +in doc/2. You need code like this in `update.secondary` (don't forget to +`chmod +x` the file): + + #!/bin/bash + + for f in $HOME/.gitolite.down $PWD/.gitolite.down + do + if [ -f $f ] + then + echo >&2 + echo '*** ABORT ***' >&2 + echo >&2 + cat $f >&2 + exit 1 + fi + done + + exit 0 + + + #### (bonus) restricted admin It's rather important to me (and presumably others in the "corporate" world) From faf1629fd83869d5b1222088fe5cceb790db9a43 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Jun 2010 06:16:13 +0530 Subject: [PATCH 382/850] better anchors in docs: changed autotoc and ran it through all docs --- README.mkd | 20 ++--- contrib/autotoc | 36 ++++---- doc/0-INSTALL.mkd | 72 ++++++++-------- doc/2-admin.mkd | 32 +++---- doc/3-faq-tips-etc.mkd | 146 ++++++++++++++------------------ doc/4-wildcard-repositories.mkd | 44 +++++----- doc/5-delegation.mkd | 16 ++-- doc/6-ssh-troubleshooting.mkd | 64 +++++++------- doc/7-install-transcript.mkd | 20 ++--- doc/9-gitolite-and-ssh.mkd | 16 ++-- doc/admin-defined-commands.mkd | 32 +++---- doc/big-config.mkd | 20 ++--- 12 files changed, 251 insertions(+), 267 deletions(-) diff --git a/README.mkd b/README.mkd index 1f6c93e..b4c0760 100644 --- a/README.mkd +++ b/README.mkd @@ -8,15 +8,15 @@ given branch. In this document: - * what - * why - * other features - * security - * contact and license + * what + * why + * other features + * security + * contact and license ---- - + ### what @@ -33,7 +33,7 @@ requiring root permissions, and with no additional software than git itself and perl. It also has several other neat features described below and elsewhere in the [doc/][docs] directory. - + ### why @@ -80,7 +80,7 @@ Gitolite does away with all this: read-write access, not only at the repository level, but at the branch level within repositories. - + ### other features @@ -114,7 +114,7 @@ detail somewhere in gitolite's [doc/][docs] subdirectory. * specify repos using patterns (patterns may include creator's name) * define powerful operations on the server side, even github-like forking - + ### security @@ -136,7 +136,7 @@ details, looking for the word "security". ---- - + ### contact and license diff --git a/contrib/autotoc b/contrib/autotoc index 36a6f61..ca85eb4 100755 --- a/contrib/autotoc +++ b/contrib/autotoc @@ -1,25 +1,31 @@ #!/usr/bin/perl -w +use strict; + # filter gitolite's mkd files through this; it's designed to be idempotent of # course -undef $/; -$doc = <>; - -$doc =~ s/^<\/a>\n\n//mg; - -@toc = $doc =~ /^###.*/mg; -$i = 0; -$doc =~ s/^###/$i++; "<\/a>\n\n###"/mge; - -$i = 0; -for (@toc) { - $i++; - s/### (.*)/ * $1<\/a>/; - s/^(#+)/' ' x length($1)/e; +sub make_anchor { + # make an anchor out of a section heading + my $sh = shift; + $sh =~ s/\W+/_/g; + $sh =~ s/^_//; + return $sh; } -$toc = "In this document:\n\n"; +undef $/; +my $doc = <>; + +$doc =~ s/^<\/a>\n\n//mg; + +my @toc = $doc =~ /^###+ .*/mg; +$doc =~ s/^(###+) (.*)/"<\/a>\n\n$1 $2"/mge; + +for (@toc) { + s/^(###+) (.*)/' ' x (length($1)-3) . ' * $2<\/a>"/e; +} + +my $toc = "In this document:\n\n"; $toc .= join("\n", @toc); $toc .= "\n\n"; diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 754f4d2..6a1a487 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -2,32 +2,32 @@ In this document: - * please read this first - * important notes - * conventions used - * requirements - * client side - * server side - * installation and setup - * (package method) directly on the server, using RPM/DEB - * (root method) directly on the server, manually, with root access - * (non-root method) directly on the server, manually, without root access - * (from-client method) install from the client to the server - * special cases -- multiple gitolite servers - * package method and root method - * from-client method - * upgrading - * uninstalling - * cleaning out a botched install - * uninstalling gitolite completely + * please read this first + * important notes + * conventions used + * requirements + * client side + * server side + * installation and setup + * (package method) directly on the server, using RPM/DEB + * (root method) directly on the server, manually, with root access + * (non-root method) directly on the server, manually, without root access + * (from-client method) install from the client to the server + * special cases -- multiple gitolite servers + * package method and root method + * from-client method + * upgrading + * uninstalling + * cleaning out a botched install + * uninstalling gitolite completely ---- - + ### please read this first - + #### important notes @@ -44,7 +44,7 @@ Please make sure you understand the following points first. To make matters worse, ssh problems in gitolite don't always look like ssh problems. See [doc/6-ssh-troubleshooting.mkd][doc6] for help. - + #### conventions used @@ -52,11 +52,11 @@ We assume the admin user is "sitaram", and his workstation is called "client". The hosting user is "git", and the server is called "server". Substitute your values as needed. - + #### requirements - + ##### client side @@ -68,7 +68,7 @@ values as needed. shell is needed * again, msysgit on Windows is fine - + ##### server side @@ -81,11 +81,11 @@ values as needed. * perl (but since git requires it anyway, you probably have it) * openssh or any ssh that can understand the `authorized_keys` file format - + ### installation and setup - + #### (package method) directly on the server, using RPM/DEB @@ -99,7 +99,7 @@ values as needed. * on the client, run `cd; git clone git@server:gitolite-admin` - + #### (root method) directly on the server, manually, with root access @@ -119,7 +119,7 @@ values as needed. * on the client, run `cd; git clone git@server:gitolite-admin` - + #### (non-root method) directly on the server, manually, without root access @@ -147,7 +147,7 @@ server so that if you screw up the keys you can still get on, or be able to * on the client, run `cd; git clone git@server:gitolite-admin` - + #### (from-client method) install from the client to the server @@ -162,11 +162,11 @@ documentation. This method is verbosely documented in [doc/7-install-transcript.mkd][doc7], including *outputs* of the commands concerned. - + ### special cases -- multiple gitolite servers - + #### package method and root method @@ -190,7 +190,7 @@ to have a command line on the server, so don't give them the passwords if you don't need to -- the pubkey will allow them to be gitolite admins on their domain, and that's quite enough for normal operations. - + #### from-client method @@ -210,7 +210,7 @@ have been created as respective clones. Or you can re-clone elsewhere: cd ~/admin1; git clone gitolite_server_1:gitolite-admin.git cd ~/admin2; git clone gitolite_server_2:gitolite-admin.git - + ### upgrading @@ -222,11 +222,11 @@ arguments! Also, remember that some new features may require additional settings in your `~/.gitolite.rc` file. - + ### uninstalling - + #### cleaning out a botched install @@ -244,7 +244,7 @@ here's how to clean the slate. * remove `~/.gitolite`, `~/.gitolite.rc` and `~/repositories/gitolite-admin.git` - + #### uninstalling gitolite completely diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 1770453..35bff40 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -5,18 +5,18 @@ In this document: - * please read this first - * adding users and repos - * other features - * moving pre-existing repos into gitolite - * specifying gitweb and daemon access - * custom hooks - * hook chaining - * custom git config + * please read this first + * adding users and repos + * other features + * moving pre-existing repos into gitolite + * specifying gitweb and daemon access + * custom hooks + * hook chaining + * custom git config ---- - + ### please read this first @@ -31,7 +31,7 @@ In this document: Once you've cloned it, you're ready to add users and repos. - + ### adding users and repos @@ -54,11 +54,11 @@ Once you've cloned it, you're ready to add users and repos. automatically be created (empty, but clonable) and users' access will be updated as needed. - + ### other features - + #### moving pre-existing repos into gitolite @@ -79,7 +79,7 @@ gitolite: `conf/gitolite.conf` in your gitolite-admin repo clone. Then add, commit, push. - + #### specifying gitweb and daemon access @@ -117,7 +117,7 @@ The "compile" script will keep these files consistent with the config settings -- this includes removing such settings/files if you remove "read" permissions for the special usernames or remove the description line. - + #### custom hooks @@ -136,7 +136,7 @@ implements all the branch-level permissions in gitolite. If you fiddle with the hooks directory, please make sure you do not mess with this file accidentally, or all your fancy per-branch permissions will stop working.** - + #### hook chaining @@ -159,7 +159,7 @@ Finally, these names (`update.secondary` and `post-update.secondary`) are merely the defaults. You can change them to anything you want; look in conf/example.gitolite.rc for details. - + #### custom git config diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 19670ce..74c1c2c 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -2,44 +2,44 @@ In this document: - * common errors and mistakes - * git version dependency - * other errors, warnings, notes... - * cloning an empty repo - * `@all` syntax for repos - * umask setting - * getting a tar file from a clone - * features - * syntax and normal usage - * simpler syntax - * one user, many keys - * security, access control, and auditing - * two levels of access rights checking - * better logging - * "exclude" (or "deny") rules - * separating delete and rewind rights - * file/dir NAME based restrictions - * delegating parts of the config file - * convenience features - * what repos do I have access to? - * including config lines from other files - * support for git installed outside default PATH - * "personal" branches - * custom hooks and custom git config - * helping with gitweb - * easier to specify gitweb "description" and gitweb/daemon access - * easier to link gitweb authorisation with gitolite - * advanced features - * repos named with wildcards - * admin defined commands - * access control for external commands - * svnserve - * design choices - * keeping the parser and the access control separate + * common errors and mistakes + * git version dependency + * other errors, warnings, notes... + * cloning an empty repo + * `@all` syntax for repos + * umask setting + * getting a tar file from a clone + * features + * syntax and normal usage + * simpler syntax + * one user, many keys + * security, access control, and auditing + * two levels of access rights checking + * better logging + * "exclude" (or "deny") rules + * separating delete and rewind rights + * file/dir NAME based restrictions + * delegating parts of the config file + * convenience features + * what repos do I have access to? + * including config lines from other files + * support for git installed outside default PATH + * "personal" branches + * custom hooks and custom git config + * helping with gitweb + * easier to specify gitweb "description" and gitweb/daemon access + * easier to link gitweb authorisation with gitolite + * advanced features + * repos named with wildcards + * admin defined commands + * access control for external commands + * svnserve + * design choices + * keeping the parser and the access control separate ---- - + ### common errors and mistakes @@ -63,17 +63,17 @@ In this document: Please see doc/6-ssh-troubleshooting.mkd for what all this means. - + ### git version dependency Gitolite (on the server) now refuses to run if git is not at least 1.6.2. - + ### other errors, warnings, notes... - + #### cloning an empty repo @@ -86,7 +86,7 @@ end hung up unexpectedly`. However, you can ignore this, since it doesn't seem to hurt anything. [Update 2009-09-14; this has been fixed in git 1.6.4.3] - + #### `@all` syntax for repos @@ -98,14 +98,14 @@ There *is* a way to use the `@all` syntax for repos also, as described in access, we do not support this. * don't try giving `@all` users some permission for `@all` repos - + #### umask setting Gitweb not able to read your repos? You can change the umask for newly created repos to something more relaxed -- see the `~/.gitolite.rc` file - + ### getting a tar file from a clone @@ -123,22 +123,18 @@ plain "git archive", because the Makefile adds a file called - - ### features Apart from the big ones listed in the top level README, and subjective ones like "better config file format", gitolite has evolved to have many useful features than the original goal of branch-level access control. - + #### syntax and normal usage - - ##### simpler syntax The basic syntax is simpler and cleaner but it goes beyond that: **you can @@ -182,9 +178,7 @@ do not worry that this causes some duplication or inefficiency. It doesn't See the "specify gitweb/daemon access" section below for one more example. - - - + ##### one user, many keys @@ -229,13 +223,11 @@ corresponding derived usernames (which is what goes into the sitaramc@gmail.com@laptop.pub sitaramc@gmail.com sitaramc@gmail.com@desktop.pub sitaramc@gmail.com - + #### security, access control, and auditing - - - + ##### two levels of access rights checking @@ -271,7 +263,7 @@ any of the refexes match, the push succeeds. If none of them match, it fails. Gitolite also allows "exclude" or "deny" rules. See later in this document for details. - + ##### better logging @@ -299,7 +291,7 @@ The other parts of the log line are the name of the repo, the refname being updated, the user updating it, and the refex pattern (from the config file) that matched, in case you need to debug the config file itself. - + ##### "exclude" (or "deny") rules @@ -350,7 +342,7 @@ And here's how it works: before the third one, and it has a `-` as the permission, so the push fails - + ##### separating delete and rewind rights @@ -389,7 +381,7 @@ message when you push, a non-existant user. [sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c# - + ##### file/dir NAME based restrictions @@ -400,7 +392,7 @@ changed, treating each filename as a "ref" to be matched. Please see `conf/example.conf` for syntax and examples. - + ##### delegating parts of the config file @@ -409,13 +401,11 @@ access control for their own pieces. See [doc/5-delegation.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/5-delegation.mkd) for details. - + #### convenience features - - - + ##### what repos do I have access to? @@ -448,13 +438,13 @@ administrator can also say things like: to get this info for other user(s). - + ##### including config lines from other files See the entry under "INCLUDE SOME OTHER FILE" in `conf/example.conf`. - + ##### support for git installed outside default PATH @@ -488,7 +478,7 @@ the full PATH in the rc file, like so: $ENV{PATH} = "/home/sitaram/bin:$ENV{PATH}"; - + ##### "personal" branches @@ -512,7 +502,7 @@ important thing is that the "branch" name should contain `/USER/` (including the slashes). At runtime this will match whoever is the current user. Access is still determined by the right hand side of course. - + ##### custom hooks and custom git config @@ -520,9 +510,7 @@ You can specify hooks that you want to propagate to all repos, as well as per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and `conf/example.conf` for details. - - - + #### helping with gitweb @@ -530,7 +518,7 @@ Although gitweb is a completely separate program, gitolite can do quite a lot to help you manage gitweb access as well; once the initial setup is complete, you can do it all from within the gitolite config file! - + ##### easier to specify gitweb "description" and gitweb/daemon access @@ -579,9 +567,7 @@ Here's an example, using really short reponames because I'm lazy: repo r2 # ...and so on... - - - + ##### easier to link gitweb authorisation with gitolite @@ -617,24 +603,24 @@ Gitweb allows you to specify a subroutine to decide on access. We use that feature and tie it to gitolite. Configuration example can be found in `contrib/gitweb/`. - + #### advanced features - + ##### repos named with wildcards Please see `doc/4-wildcard-repositories.mkd` for all the details. - + ##### admin defined commands This requires the wildcards feature to be enabled, but is then an extremely powerful feature. See `doc/admin-defined-commands.mkd`. - + ##### access control for external commands @@ -660,8 +646,6 @@ Commands implemented so far are: - - ###### svnserve If you are transitioning from SVN to gitolite, and have a lot of users using @@ -672,11 +656,11 @@ system. Assuming you installed gitolite to the same user as the one you used for SVN, SVN connectivity will be retained, and users will be able to use both SVN and git using the same SSH configuration. - + ### design choices - + #### keeping the parser and the access control separate diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 4393e5a..f5a66f0 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -19,17 +19,17 @@ workarounds I may not have the time to code it right away. In this document: - * rc file setting required - * Wildcard repos - * Wildcard repos with creator name in them - * Wildcard repos without creator name in them - * Side-note: Line-anchored regexes - * Contrast with refexes - * Handing out rights to wildcard-matched repos - * setting a gitweb description for a wildcard-matched repo - * reporting - * other issues and discussion - * how it actually works + * rc file setting required + * Wildcard repos + * Wildcard repos with creator name in them + * Wildcard repos without creator name in them + * Side-note: Line-anchored regexes + * Contrast with refexes + * Handing out rights to wildcard-matched repos + * setting a gitweb description for a wildcard-matched repo + * reporting + * other issues and discussion + * how it actually works ---- @@ -37,7 +37,7 @@ This document is mostly "by example". ---- - + ### rc file setting required @@ -45,7 +45,7 @@ This feature requires that you set `$GL_WILDREPOS` to "1" in `~/.gitolite.rc` on the server. Please search for that variable and see comments around it in `conf/example.gitolite.rc` for more information on this. - + ### Wildcard repos @@ -53,7 +53,7 @@ Which of these alternatives you choose depends on your needs, and the social aspects of your environment. The first one is a little more rigid, making it harder to make mistakes, and the second is more flexible and trusting. - + #### Wildcard repos with creator name in them @@ -79,7 +79,7 @@ new repo, as user "u4" (a student): Notice the *two* empty repo inits, and the order in which they occur ;-) - + #### Wildcard repos without creator name in them @@ -106,7 +106,7 @@ and have a TA create the repos in advance. In either case, they could then use the `setperms` feature to specify which users are "READERS" and which are "WRITERS". See later for details. - + ### Side-note: Line-anchored regexes @@ -123,7 +123,7 @@ But you may be surprised to find that it does not match even `^assignments/S[0-9]+/A[0-9]+$` -- notice the line beginning and ending metacharacters. - + #### Contrast with refexes @@ -135,7 +135,7 @@ if no one will actually push such a branch! You can anchor both sides if you really care, by using `master$` instead of `master`, but that is *not* the default for refexes. - + ### Handing out rights to wildcard-matched repos @@ -182,14 +182,14 @@ The following points are important: * whoever you specify as "R" will match the special user READERS. "RW" will match WRITERS. - + ### setting a gitweb description for a wildcard-matched repo Similar to the getperm/setperm commands, there are the getdesc/setdesc commands, thanks to Teemu. - + ### reporting @@ -215,7 +215,7 @@ Push the config, then try ssh gitolite expand - + ### other issues and discussion @@ -237,7 +237,7 @@ Push the config, then try of the time, it won't be difficult; the fixed prefix will usually be different anyway so there won't be overlaps. - + ### how it actually works diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index 79864c0..8cc341f 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -6,14 +6,14 @@ In this document: - * lots of repos, lots of users - * splitting up the set of repos into groups - * delegating ownership of groups of repos - * security/philosophy note + * lots of repos, lots of users + * splitting up the set of repos into groups + * delegating ownership of groups of repos + * security/philosophy note ---- - + ### lots of repos, lots of users @@ -47,7 +47,7 @@ access control rules for a set of repos they have been given authority for. It's easier to show how it all works with an example instead of long descriptions. - + ### splitting up the set of repos into groups @@ -63,7 +63,7 @@ or repos, same syntax). So the basic idea is that the main config file # any other config as usual, including access control lines for any of the # above projects or groups - + ### delegating ownership of groups of repos @@ -117,7 +117,7 @@ to the bottom of the main file. ---- - + ### security/philosophy note diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 3341b8a..8fd4edd 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -2,21 +2,21 @@ In this document: - * the most common problems that an admin will see - * basic ssh troubleshooting - * passphrases versus passwords - * ssh-agent problems - * basic ssh troubleshooting for the main admin - * basic ssh troubleshooting for a normal user - * windows issues - * details - * files on the server - * files on client - * why two keys on client - * some other tips and tricks - * giving shell access to gitolite users - * losing your admin key - * simulating ssh-copy-id + * the most common problems that an admin will see + * basic ssh troubleshooting + * passphrases versus passwords + * ssh-agent problems + * basic ssh troubleshooting for the main admin + * basic ssh troubleshooting for a normal user + * windows issues + * details + * files on the server + * files on client + * why two keys on client + * some other tips and tricks + * giving shell access to gitolite users + * losing your admin key + * simulating ssh-copy-id ---- @@ -33,7 +33,7 @@ In addition to both these documents, there's now a program called `sshkeys-lint` that you can run on your client. Run it without arguments to get help on how to run it and what inputs it needs. - + ### the most common problems that an admin will see @@ -85,7 +85,7 @@ the repo and clones ok. But when you push, gitolite's **update hook** kicks in, and fails to run because you some of the environment variables it is expecting are not present. - + ### basic ssh troubleshooting @@ -98,7 +98,7 @@ username* of the admin. Unless specifically mentioned, all these commands are run on the user's or admin's workstation, not on the server. - + #### passphrases versus passwords @@ -119,7 +119,7 @@ The second is to use `ssh-agent` (or `keychain`, which in turn uses `ssh-agent`) or something like that to manage your keys. Other than the next section, further discussion of this is out of scope of this document. - + #### ssh-agent problems @@ -145,7 +145,7 @@ all, ssh will only use those keys. Even if you explicitly specify an unlisted key using `ssh -i` or an `identityfile` directive in the config file, it won't use it. - + #### basic ssh troubleshooting for the main admin @@ -215,7 +215,7 @@ from scratch: That's a long sequence but it should work. - + #### basic ssh troubleshooting for a normal user @@ -234,7 +234,7 @@ you had command line access to the server *before* you were added as a gitolite user. If you send that same key to your gitolite admin to include in the admin repo, it won't work. For reasons why, see below. - + ### windows issues @@ -252,13 +252,11 @@ link, or in contrib/ if it's a longer piece of text. - - ### details Here's how it all hangs together. - + #### files on the server @@ -290,7 +288,7 @@ Here's how it all hangs together. argument `sitaram`. This is how gitolite is invoked, (and is told the user logging in is "sitaram"). - + #### files on client @@ -360,9 +358,7 @@ Here's how it all hangs together. now works as expected, invoking the special keypair instead of the default one. - - - + #### why two keys on client @@ -429,13 +425,11 @@ that should have enough info to get you going (but it helps to know ssh well): That should do it. - - - + ### some other tips and tricks - + #### giving shell access to gitolite users @@ -463,7 +457,7 @@ access would not manage to get himself shell access. Giving someone shell access requires that you should have shell access in the first place, so the simplest way is to enable it from the server side only. - + #### losing your admin key @@ -473,7 +467,7 @@ gitolite-admin repository with a fresh key, take a look at the of course. The top of the script has useful information on how to use it and what it needs. - + #### simulating ssh-copy-id diff --git a/doc/7-install-transcript.mkd b/doc/7-install-transcript.mkd index 3b5c6c7..1ebf116 100644 --- a/doc/7-install-transcript.mkd +++ b/doc/7-install-transcript.mkd @@ -20,15 +20,15 @@ you have to run it on your workstation, NOT on the server!** In this document: - * create userids on server and client (optional) - * get pubkey access from client to server - * get gitolite source - * install gitolite - * examine what you have + * create userids on server and client (optional) + * get pubkey access from client to server + * get gitolite source + * install gitolite + * examine what you have ---- - + ### create userids on server and client (optional) @@ -75,7 +75,7 @@ because I'm not showing the actual "vi" session): ---- - + ### get pubkey access from client to server @@ -126,7 +126,7 @@ Double check to make sure you can log on to `git@server` without a password: ---- - + ### get gitolite source @@ -138,7 +138,7 @@ Double check to make sure you can log on to `git@server` without a password: Receiving objects: 100% (1157/1157), 270.08 KiB | 61 KiB/s, done. Resolving deltas: 100% (756/756), done. - + ### install gitolite @@ -209,7 +209,7 @@ install mode that allows you to change the defaults etc. ---- - + ### examine what you have diff --git a/doc/9-gitolite-and-ssh.mkd b/doc/9-gitolite-and-ssh.mkd index 85db02c..a03636f 100644 --- a/doc/9-gitolite-and-ssh.mkd +++ b/doc/9-gitolite-and-ssh.mkd @@ -13,14 +13,14 @@ blaming ***git/gitolite*** for whatever is going wrong with your setup :-) In this document: - * ssh basics - * how does gitolite use all this ssh magic? - * restricting shell access/distinguishing one user from another - * restricting branch level actions + * ssh basics + * how does gitolite use all this ssh magic? + * restricting shell access/distinguishing one user from another + * restricting branch level actions ---- - + ### ssh basics @@ -88,7 +88,7 @@ from somewhere, or maybe buy the OReilly ssh book. **This is the backbone of what makes gitolite work; please make sure you understand this** - + ### how does gitolite use all this ssh magic? @@ -98,7 +98,7 @@ These are two different questions you ought to be having by now: logging in as the same remote user "git" * how does it restrict what I can do within a repository - + #### restricting shell access/distinguishing one user from another @@ -131,7 +131,7 @@ at its config file, and either allows or rejects the request. But this cannot differentiate between different branches within a repo; that has to be done separately. - + #### restricting branch level actions diff --git a/doc/admin-defined-commands.mkd b/doc/admin-defined-commands.mkd index b5e6f77..7db971d 100644 --- a/doc/admin-defined-commands.mkd +++ b/doc/admin-defined-commands.mkd @@ -11,18 +11,18 @@ There may be other such **WARNING** sections below. **Read all of them**. In this document: - * background - * setting it up - * anatomy of a command - * example uses and sample commands in contrib - * fork - * rmrepo - * enable/disable push access temporarily - * (bonus) restricted admin + * background + * setting it up + * anatomy of a command + * example uses and sample commands in contrib + * fork + * rmrepo + * enable/disable push access temporarily + * (bonus) restricted admin ---- - + ### background @@ -62,7 +62,7 @@ What we want now is more than that, as you'll see in the examples below. And I'm sure if you think of more uses you'll send them to me as "contrib" entries, right? - + ### setting it up @@ -82,7 +82,7 @@ to inadvertently *hide* some of the "official" commands (like "info", executable files with those names in this directory. So don't do that -- you have been warned!** - + ### anatomy of a command @@ -137,11 +137,11 @@ convenient. See any of the other samples for how to use it. If you don't like this, roll your own. If you don't like bash, do the eqvt in your language of choice. - + ### example uses and sample commands in contrib - + #### fork @@ -176,7 +176,7 @@ So now we have an actual command to just create a repo and do nothing else: `ssh git@server git-init \'reponame\'`. [Yes; those single quotes are required. Deal with it.] - + #### rmrepo @@ -188,7 +188,7 @@ Use it like this: The script checks to make sure that the repo being deleted was *created* by the user invoking it. - + #### enable/disable push access temporarily @@ -226,7 +226,7 @@ in doc/2. You need code like this in `update.secondary` (don't forget to exit 0 - + #### (bonus) restricted admin diff --git a/doc/big-config.mkd b/doc/big-config.mkd index 5531167..278c152 100644 --- a/doc/big-config.mkd +++ b/doc/big-config.mkd @@ -2,13 +2,13 @@ In this document: - * when/why do we need it? - * how do we use it? - * summary of settings in RC file - * what are the downsides? - * (extra coolness) usergroups and LDAP/similar tools + * when/why do we need it? + * how do we use it? + * summary of settings in RC file + * what are the downsides? + * (extra coolness) usergroups and LDAP/similar tools - + ### when/why do we need it? @@ -95,7 +95,7 @@ Phew! You can imagine what that does when you have 10,000 users and 10,000 repos. Let's just say it's not pretty :) - + ### how do we use it? @@ -142,7 +142,7 @@ configuration, the compiled file looks like this: That's a lot smaller, and allows orders of magintude more repos and groups to be supported. - + ### summary of settings in RC file @@ -162,7 +162,7 @@ you push the gitolite-admin repo with changes. [gw]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gitweb - + ### what are the downsides? @@ -176,7 +176,7 @@ subset of the allowed @fragname, which would work normally, do not work now). (If you didn't understand all that, you're probably not using delegation, so feel free to ignore it!) - + ### (extra coolness) usergroups and LDAP/similar tools From 805050a12956d4edfa958967f45a9a669f3a79b7 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Jun 2010 16:27:33 +0530 Subject: [PATCH 383/850] remove a few needless bashisms... Note: "able" still needs bash but it's an easy fix if you need to use it on a bash-challenged machine and care enough --- contrib/adc/able | 8 ++++---- contrib/adc/adc.common-functions | 8 ++++---- contrib/adc/fork | 6 +++--- contrib/adc/restrict-admin | 4 ++-- contrib/adc/rmrepo | 4 ++-- src/gl-emergency-addkey | 4 ++-- src/gl-system-install | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contrib/adc/able b/contrib/adc/able index a776a3a..1b73819 100755 --- a/contrib/adc/able +++ b/contrib/adc/able @@ -1,17 +1,17 @@ #!/bin/bash -# WARNING: USES BASH FEATURES... +# WARNING: USES BASH FEATURES TO AVOID A TEMP FILE; CAN BE FIXED IF NEEDED . $(dirname $0)/adc.common-functions get_rights_and_owner gitolite-admin -[[ -z $perm_write ]] && die "just *what* are you trying to pull, young man?" +[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?" op=$1 shift locs= -while [[ -n $1 ]] +while [ -n "$1" ] do case $1 in '@all' ) @@ -33,7 +33,7 @@ case $op in done ;; dis|disable ) - [[ -t 0 ]] && printf message: + # bashism read msg <<<$(cat) for l in $locs do diff --git a/contrib/adc/adc.common-functions b/contrib/adc/adc.common-functions index 2631ed2..0d2878c 100644 --- a/contrib/adc/adc.common-functions +++ b/contrib/adc/adc.common-functions @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # please make sure this file is NOT chmod +x @@ -11,7 +11,7 @@ get_rights_and_owner() { # set shell variables as needed owner=${ans#* } rights=${ans% *} - [[ $rights =~ C ]] && perm_create=yes || perm_create= - [[ $rights =~ R ]] && perm_read=yes || perm_read= - [[ $rights =~ W ]] && perm_write=yes || perm_write= + echo $rights | grep C >/dev/null 2>&1 && perm_create=yes || perm_create= + echo $rights | grep R >/dev/null 2>&1 && perm_read=yes || perm_read= + echo $rights | grep W >/dev/null 2>&1 && perm_write=yes || perm_write= } diff --git a/contrib/adc/fork b/contrib/adc/fork index 758675f..d5b065a 100755 --- a/contrib/adc/fork +++ b/contrib/adc/fork @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh from=$1 to=$2 @@ -10,10 +10,10 @@ to=$2 # have a new "git-init" command! get_rights_and_owner $from -[[ -z $perm_read ]] && die "no read permissions on $from" +[ -z "$perm_read" ] && die "no read permissions on $from" get_rights_and_owner $to -[[ -z $perm_create ]] && die "no create permissions on $to" +[ -z "$perm_create" ] && die "no create permissions on $to" # let gitolite create the repo first SSH_ORIGINAL_COMMAND="git-init '$to'" $GL_BINDIR/gl-auth-command $GL_USER diff --git a/contrib/adc/restrict-admin b/contrib/adc/restrict-admin index 97ad0d1..8d3d892 100755 --- a/contrib/adc/restrict-admin +++ b/contrib/adc/restrict-admin @@ -1,9 +1,9 @@ -#!/bin/bash +#!/bin/sh . $(dirname $0)/adc.common-functions get_rights_and_owner gitolite-admin -[[ -z $perm_write ]] && die "just *what* are you trying to pull, young man?" +[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?" # and here you let them do the dangerous stuff echo "+rm -rf $GL_REPO_BASE_ABS" diff --git a/contrib/adc/rmrepo b/contrib/adc/rmrepo index fa2554f..e1c288c 100755 --- a/contrib/adc/rmrepo +++ b/contrib/adc/rmrepo @@ -1,11 +1,11 @@ -#!/bin/bash +#!/bin/sh . $(dirname $0)/adc.common-functions delete=$1 get_rights_and_owner $delete -[[ $owner == $GL_USER ]] || die "$delete is not yours to delete!" +[ "$owner" = "$GL_USER" ] || die "$delete is not yours to delete!" cd $GL_REPO_BASE_ABS rm -rf $delete.git diff --git a/src/gl-emergency-addkey b/src/gl-emergency-addkey index d488798..f10027b 100755 --- a/src/gl-emergency-addkey +++ b/src/gl-emergency-addkey @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # what/why: re-establish gitolite admin access when admin key(s) lost # where: on server (NOT client!) @@ -20,7 +20,7 @@ # ENDHELP -[[ -z $1 ]] && { perl -pe "s(\\\$0)($0); last if /ENDHELP/" < $0; exit 1; } +[ -z "$1" ] && { perl -pe "s(\\\$0)($0); last if /ENDHELP/" < $0; exit 1; } set -e diff --git a/src/gl-system-install b/src/gl-system-install index b71cde9..5db21f3 100755 --- a/src/gl-system-install +++ b/src/gl-system-install @@ -39,7 +39,7 @@ if git rev-parse --is-inside-work-tree >/dev/null 2>&1 then git describe --tags --long HEAD 2>/dev/null > conf/VERSION || die "git describe failed -- this should not happen!" else - [[ -f conf/VERSION ]] || echo '(unknown)' > conf/VERSION + [ -f conf/VERSION ] || echo '(unknown)' > conf/VERSION fi cp -R conf/* $gl_conf_dir || die "cp conf/* to $gl_conf_dir failed" From 18267706dbee287db70f491d0581d1bb329d7753 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Jun 2010 23:31:28 +0530 Subject: [PATCH 384/850] doc/0: a minor clarification and a minor re-ordering --- doc/0-INSTALL.mkd | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 6a1a487..6326641 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -111,6 +111,8 @@ values as needed. cd $HOME git clone git://github.com/sitaramc/gitolite gitolite-source cd gitolite-source + # now checkout whatever branch you want; for early adopters I suggest + # "pu", as in "git checkout -t origin/pu" for recent gits mkdir -p /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks src/gl-system-install /usr/local/bin /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks @@ -131,20 +133,21 @@ server so that if you screw up the keys you can still get on, or be able to * from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server. Put it in `/tmp/sitaram.pub`. + * if `$HOME/bin` is not on the default PATH, fiddle with your `.bashrc` or + `.bash_profile` or similar files and add it somehow. + * (U) on the server, as "git", do the following: cd $HOME git clone git://github.com/sitaramc/gitolite gitolite-source + # now checkout whatever branch you want; for early adopters I suggest + # "pu", as in "git checkout -t origin/pu" for recent gits cd gitolite-source mkdir -p $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks src/gl-system-install $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks * on the server, still as "git", run `gl-setup /tmp/sitaram.pub`. - * if `$HOME/bin` is not on the default PATH, fiddle with your `.bashrc` or - `.bash_profile` files and add it somehow. You can also try adding - `$GIT_PATH = "$ENV{HOME}/bin";` to `~/.gitolite.rc` on the server. - * on the client, run `cd; git clone git@server:gitolite-admin` From 6d32e4e9203f5cbfcba1a27a061a748aa1b69f90 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 1 Jun 2010 21:13:38 +0530 Subject: [PATCH 385/850] now you can disallow creation of new refs if you like see doc/3 for details (look for "separating delete and rewind rights" ---- and for gerrit, this is one more thing it can do that we can too ;-) [the original text was somewhat misleading. We mean "prevent someone from creating a branch that they have permissions to push". That is what is now possible, where it was not possible before.] --- contrib/gerrit.mkd | 4 ---- doc/3-faq-tips-etc.mkd | 35 +++++++++++++++++++++++++++++++++++ hooks/common/update | 2 ++ src/gitolite.pm | 2 +- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/contrib/gerrit.mkd b/contrib/gerrit.mkd index 679f1a9..e55cf47 100644 --- a/contrib/gerrit.mkd +++ b/contrib/gerrit.mkd @@ -92,10 +92,6 @@ review stuff :) * gitolite doesnt do anything special to signed or annotated tags - * gitolite always allows creating a branch. The only way to prevent that is - to list out allowed branches explicitly (make sure you end the refex with - a `$`!). - * Force push is the same as delete: historically (and by default, even now) gitolite does the same . However, I've only recently (and somewhat reluctantly) changed gitolite to allow treating these two separately. diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 74c1c2c..fc8663f 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -18,6 +18,7 @@ In this document: * better logging * "exclude" (or "deny") rules * separating delete and rewind rights + * separating create and push rights * file/dir NAME based restrictions * delegating parts of the config file * convenience features @@ -381,6 +382,40 @@ message when you push, a non-existant user. [sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c# + + +##### separating create and push rights + +[note: the documentation took longer to write than the code ;-)] + +Now you can disallow creation of new refs if you like. + +Normally, when you try to update a ref (push a branch or a tag), it's checked +against all the refexes, and if none of them match for the operation you're +trying (W or +), or it matches a "-", the operation is denied. + +That is, most refs are default "deny". + +`CREATE_REF` is a "fake" refex that controls the ability to *create* a branch, +even if you are allowed to *push* changes to it. + +The right to create a ref (i.e., push a brand new one), however, defaults to +"accept" unless a deny rule is found. This is mainly for backward compat +reasons, but also because this feature is rarely needed, so there's no point +burdening everyone with having to create the opposite rule. + +So if you want to prevent someone from *creating* any branches that they +otherwise *can* push, make sure that the first rule that applies to them is a +`- CREATE_REF` rule, like line #3 below: + + repo foo + RW+ = @leads + - CREATE_REF = @devs + RW+ = @devs + +One side effect is that you now can no longer have an *actual* branch called +`CREATE_REF`. Oh well, into each life some rain must fall! + ##### file/dir NAME based restrictions diff --git a/hooks/common/update b/hooks/common/update index f55e2cc..219157d 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -82,6 +82,8 @@ push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; # been specified my @refs = ($ref); # the first ref to check is the real one +# if a new ref is being created, add a special ref to record that +push @refs, 'refs/heads/CREATE_REF' if $oldsha eq '0' x 40; # because making it work screws up efficiency like no tomorrow... if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) { # this is special to git -- the hash of an empty tree diff --git a/src/gitolite.pm b/src/gitolite.pm index bdc0db8..bb4fb5f 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -87,7 +87,7 @@ sub check_ref { # as far as *this* ref is concerned we're ok return $refex if ($ar->[2] =~ /\Q$perm/); } - die "$perm $ref $repo $ENV{GL_USER} DENIED by fallthru\n"; + die "$perm $ref $repo $ENV{GL_USER} DENIED by fallthru\n" unless $ref eq 'refs/heads/CREATE_REF'; } # ln -sf :-) From 080ec22ae9090fe89a4eacf3fe732f3282eb87b8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 2 Jun 2010 10:48:28 +0530 Subject: [PATCH 386/850] compile: kill spurious "user ... not in config" warnings this happens when users are given rights to a repo via a groupname, and GL_BIG_CONFIG is in effect --- src/gl-compile-conf | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index f9b5786..334259e 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -267,7 +267,15 @@ sub parse_conf_file } for my $user (@users) { - $user_list{$user}++; # only to catch lint, see later + # lint check, to catch pubkey/username typos + if ($user =~ /^@/ and $user ne '@all') { + # this is a usergroup, not a normal user; happens with GL_BIG_CONFIG + if (exists $groups{$user}) { + $user_list{$_}++ for keys %{ $groups{$user} }; + } + } else { + $user_list{$user}++; + } # for 1st level check (see faq/tips doc) $repos{$repo}{C}{$user} = 1, next if $perms eq 'C'; From ba8094d6f59000d142b117277fec81d91806ea10 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 12 Jun 2010 09:16:32 +0530 Subject: [PATCH 387/850] report_basic forgot how to display wildcards during big-config change in addition, due to "+" becoming a valid character in a normal reponame, (think gtk+, etc), the pattern repo dev/CREATOR/.+ doesn't look like a wildcard repo anymore, so we add an extra check that if CREATOR is mentioned, it *is* a wildcard. This has been added *only* to the report_basic function; it doesn't really matter anywhere else. --- src/gitolite.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index bb4fb5f..776c800 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -324,9 +324,10 @@ sub report_basic &report_version($GL_ADMINDIR, $user); print "\rthe gitolite config gives you the following access:\r\n"; for my $r (sort keys %repos) { - if ($r =~ $REPONAME_PATT) { + if ($r =~ $REPONAME_PATT and $r !~ /\bCREAT[EO]R\b/) { &parse_acl($GL_CONF_COMPILED, $r, "NOBODY", "NOBODY", "NOBODY"); } else { + $r =~ s/\bCREAT[EO]R\b/$user/g; &parse_acl($GL_CONF_COMPILED, $r, $ENV{GL_USER}, "NOBODY", "NOBODY"); } # @all repos; meaning of read/write flags: From 0add3d3de7d5afbbeea6f0359635f98e1a153e1c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 12 Jun 2010 11:03:53 +0530 Subject: [PATCH 388/850] finally, open up my secret test scripts... ...after getting rid of most of the hardcoding (though not all!) --- t/README.mkd | 79 +++++++++++++++++ t/basic.conf | 7 ++ t/cleanout-gitolite | 22 +++++ t/install-gitolite | 36 ++++++++ t/keys/config | 15 ++++ t/keys/u1 | 27 ++++++ t/keys/u1.pub | 1 + t/keys/u2 | 27 ++++++ t/keys/u2.pub | 1 + t/keys/u3 | 27 ++++++ t/keys/u3.pub | 1 + t/keys/u4 | 27 ++++++ t/keys/u4.pub | 1 + t/keys/u5 | 27 ++++++ t/keys/u5.pub | 1 + t/keys/u6 | 27 ++++++ t/keys/u6.pub | 1 + t/out/t01-repo-groups.1 | 99 +++++++++++++++++++++ t/out/t01-repo-groups.2 | 63 ++++++++++++++ t/out/t02-user-groups.1 | 66 ++++++++++++++ t/out/t02-user-groups.2 | 71 +++++++++++++++ t/out/t04-wild1.1 | 5 ++ t/out/t04-wild1.2 | 4 + t/rollback | 11 +++ t/rollback.server | 5 ++ t/t-fedora-big-config | 24 ++++++ t/t00-initial | 70 +++++++++++++++ t/t01-repo-groups | 58 +++++++++++++ t/t02-user-groups | 64 ++++++++++++++ t/t03-branch-permissions | 20 +++++ t/t03a-branch-permissions | 177 ++++++++++++++++++++++++++++++++++++++ t/t04-wild | 18 ++++ t/t04a-wild-all | 144 +++++++++++++++++++++++++++++++ t/t04a-wild-students | 142 ++++++++++++++++++++++++++++++ t/t05-delegation | 11 +++ t/t05a-delegation | 95 ++++++++++++++++++++ t/t09-oldtests | 11 +++ t/t09a-oldtests | 50 +++++++++++ t/t50-sequence-test | 93 ++++++++++++++++++++ t/t51-personal-branches | 88 +++++++++++++++++++ t/t52-deny-create-ref | 51 +++++++++++ t/test-driver.sh | 160 ++++++++++++++++++++++++++++++++++ t/update-gitolite | 14 +++ 43 files changed, 1941 insertions(+) create mode 100644 t/README.mkd create mode 100644 t/basic.conf create mode 100755 t/cleanout-gitolite create mode 100755 t/install-gitolite create mode 100644 t/keys/config create mode 100644 t/keys/u1 create mode 100644 t/keys/u1.pub create mode 100644 t/keys/u2 create mode 100644 t/keys/u2.pub create mode 100644 t/keys/u3 create mode 100644 t/keys/u3.pub create mode 100644 t/keys/u4 create mode 100644 t/keys/u4.pub create mode 100644 t/keys/u5 create mode 100644 t/keys/u5.pub create mode 100644 t/keys/u6 create mode 100644 t/keys/u6.pub create mode 100644 t/out/t01-repo-groups.1 create mode 100644 t/out/t01-repo-groups.2 create mode 100644 t/out/t02-user-groups.1 create mode 100644 t/out/t02-user-groups.2 create mode 100644 t/out/t04-wild1.1 create mode 100644 t/out/t04-wild1.2 create mode 100755 t/rollback create mode 100755 t/rollback.server create mode 100644 t/t-fedora-big-config create mode 100644 t/t00-initial create mode 100644 t/t01-repo-groups create mode 100644 t/t02-user-groups create mode 100644 t/t03-branch-permissions create mode 100644 t/t03a-branch-permissions create mode 100644 t/t04-wild create mode 100644 t/t04a-wild-all create mode 100644 t/t04a-wild-students create mode 100644 t/t05-delegation create mode 100644 t/t05a-delegation create mode 100644 t/t09-oldtests create mode 100644 t/t09a-oldtests create mode 100644 t/t50-sequence-test create mode 100644 t/t51-personal-branches create mode 100644 t/t52-deny-create-ref create mode 100755 t/test-driver.sh create mode 100755 t/update-gitolite diff --git a/t/README.mkd b/t/README.mkd new file mode 100644 index 0000000..c90d016 --- /dev/null +++ b/t/README.mkd @@ -0,0 +1,79 @@ +# notes on the testing setup + +In this document: + + * terminology + * notes and background + * quick instructions for running the test suite + * instructions for adding new tests + + + +### terminology + + #define PW "patches welcome!" + #define TODO PW + + + +### notes and background + + * all testing is done on one machine, using 2 userids + + * test driver exits on the *first* failed test; no fancy counting here. + (PW). + + * installs are done using "gl-easy-install". As such, this test suite is + mainly meant for testing **the core (access control) functionality**, and + will not help you test the install/upgrade parts themselves. Those are a + lot more difficult to test in an automated fashion, but luckily they also + change infrequently and can easily be tested manually when they do. + Errors in those are much more visible too. (PW). + + * the test driver has evolved as new scripts were added; you will see that + older scripts are a little less sophisticated. + + + +### quick instructions for running the test suite + + * create two brand new user IDs: tester and gitolite-test + * these are hard-coded, sorry. (PW) + * give them some passwords + * allow ssh into gitolite-test in `/etc/ssh/sshd_config`; preferably use + a line like `AllowUsers gitolite-test@127.0.0.1` so that no one can + ssh in from outside + + * prepare/push a bare clone of gitolite itself on /tmp so that the "tester" + userid can grab it painlessly + + cd your-gitolite-working-repo + git clone --bare $PWD /tmp/gitolite # first time + git push -f --all /tmp/gitolite # subsequent times + + * "su" to "tester" and clone/fetch the gitolite source: + + cd $HOME; git clone /tmp/gitolite # first time + cd gitolite; git fetch origin # subsequent times + + * checkout and "install" the branch you want to test (install from "tester" + userid to "gitolite-test" userid). Example, if you want to test "pu" + branch: + + git checkout -t origin/pu # if needed + git checkout -f pu; git reset --hard origin/pu # subsequent times + cd t # THIS IS IMPORTANT (patches welcome, or will be fixed eventually!) + ./install-gitolite + + * run all or some of the tests + + ./test-driver.sh + # or + ./test-driver.sh t51 + + + +### instructions for adding new tests + +(TODO) + diff --git a/t/basic.conf b/t/basic.conf new file mode 100644 index 0000000..93bf22b --- /dev/null +++ b/t/basic.conf @@ -0,0 +1,7 @@ +repo gitolite-admin + RW+ = tester + +repo testing + RW+ = @all + + diff --git a/t/cleanout-gitolite b/t/cleanout-gitolite new file mode 100755 index 0000000..4feaa06 --- /dev/null +++ b/t/cleanout-gitolite @@ -0,0 +1,22 @@ +#!/bin/bash + +export TESTDIR=$PWD + +[[ -f /tmp/$1.tar ]] || { echo need tar file 2>&1; exit 1; } + +# blank out the client side +rm -rf ~/gitolite-admin +# make sure we have the u1-u6 keys and the config to refer to them +cp $TESTDIR/keys/config ~/.ssh +cp $TESTDIR/keys/u[1-6]* ~/.ssh +chmod 755 ~/.ssh ~/.ssh/config ~/.ssh/*pub +chmod 600 ~/.ssh/u? + +# blank out the server side +echo the next command MAY ask for a password +ssh gitolite-test@localhost rm -rf .ssh .gitolite .gitolite.rc repositories gitolite-install +echo the next command SHOULD ask for a password +ssh-copy-id -i ~/.ssh/id_rsa gitolite-test@localhost +echo the next command should NOT ask for a password + +cd ~/gitolite diff --git a/t/install-gitolite b/t/install-gitolite new file mode 100755 index 0000000..fd02b85 --- /dev/null +++ b/t/install-gitolite @@ -0,0 +1,36 @@ +#!/bin/bash + +export TESTDIR=$PWD + +# prepare local ssh +mkdir -p $HOME/.ssh; chmod go-rx $HOME/.ssh +[ -f "$HOME/.ssh/id_rsa" ] || ssh-keygen -q -N "" -t rsa -f $HOME/.ssh/id_rsa + +# blank out the client side +rm -rf ~/gitolite-admin +# make sure we have the u1-u6 keys and the config to refer to them +cp $TESTDIR/keys/config ~/.ssh +cp $TESTDIR/keys/u[1-6]* ~/.ssh +chmod 755 ~/.ssh ~/.ssh/config ~/.ssh/*pub +chmod 600 ~/.ssh/u? + +# blank out the server side +echo the next command MAY ask for a password +ssh gitolite-test@localhost rm -rf .ssh .gitolite .gitolite.rc repositories gitolite-install +echo the next command SHOULD ask for a password +ssh-copy-id -i ~/.ssh/id_rsa gitolite-test@localhost +echo the next command should NOT ask for a password + +# install it +../src/gl-easy-install -q gitolite-test localhost tester + +# add 6 keys +cd ~/gitolite-admin +cp $TESTDIR/keys/u*pub keydir +git add keydir; git commit -m 'added 6 keys' +git push + +# make the rollback.tar files on both sides +cd +tar cf rollback.tar gitolite-admin +ssh gitolite-test@localhost tar cf rollback.tar .ssh .gitolite .gitolite.rc repositories gitolite-install diff --git a/t/keys/config b/t/keys/config new file mode 100644 index 0000000..7330f7b --- /dev/null +++ b/t/keys/config @@ -0,0 +1,15 @@ +host u? + user gitolite-test + hostname localhost +host u1 + identityfile ~/.ssh/u1 +host u2 + identityfile ~/.ssh/u2 +host u3 + identityfile ~/.ssh/u3 +host u4 + identityfile ~/.ssh/u4 +host u5 + identityfile ~/.ssh/u5 +host u6 + identityfile ~/.ssh/u6 diff --git a/t/keys/u1 b/t/keys/u1 new file mode 100644 index 0000000..43410d0 --- /dev/null +++ b/t/keys/u1 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoAIBAAKCAQEAnqiW8xCSfg/JO7acBvarVghAyM2Xw2ZzwaQN5xOg/XOSt2wV +5wsT32hmPg/v8NqXcjKXMyI76ZO4U9To9yDvs6xKC9NwFmV6prjf6IC2gryykg2L +BvWKIrcr/tNBnKgDAs7SDtf1EEN5Byf0mTcy9c9eVMsxvwm3oXqXKCm1YkKeaWOE +d0xuRMRAVHlfon4M67r/heDZho9QLwvyDhorefhoLGOzrUbsXbcTHAEkt+m56swz +7d7yZVSXckqrQ+KyY6cVjm1kdBvO7B34TpjMYubv4a3WPC98QUAFSRir3+HW43JF +U/ty4Z7pCbpDBPctvg1fdCzIx1sg54zE6MZGnQIBIwKCAQBoQubaPhcfo/lEf1CW +3Jx6XTHjCsLQ3Oz7l7FdVgq1LrDe70GX1BRfnGBx7TdGgQRvnZaPUQLMsYfCD5HG ++GMBCtGSviUWCCwHKQgrEsEUFZnq8vT18c/Nq5HwmXRCX8d2366cCkH0vp/9Y2Y4 +zICS51s/CS9Rp0zJM01jiR9sc+LG9cz7pI8nk7jaCjhzV/229mQC5/1TxDqgdafH +/27cdtA0sqbG7lvL6k5qCYM+C96pOjmqap7x6YSMlUbcCKL1HLWzLsqMBjbgtm2M +jHB3Kv3aDk5Mf2Lj1G4Bauw/NCedoQ+bj4LCXfYIR5BKNSAmDigZitBMRHOInBEl +O7drAoGBAM9BTk9KYtS3yXmz/GVoNpgTZ1IFF90hjAhkPN5iFqOKL/f43U9Gn5gK +9wJp68FJbpB7V8qH2shI8S0jv3AJicH8MG3RHA2FSMjUTy4nKBP835OKxgl49J87 +OZNigS8BFbWKm5o0Wlm17H4GH6sEKgt4dFOelHmxO0H/4L1r0GGZAoGBAMP5UUAH +BXHLMD4GxnfNieegg7EjUbKMf6CqHAk44wCgS0mgjMrIoMiiAnf6djeKf1wrJvBB +S6eK3qu7ZUnVQKqDKZM9WAxyyZExKHx+FXFt51R6YrnJQVzMuA1Im7xSGJcmL30c +pVIwcVjKe5sKT8TUCRGYdyhfSGa+vok5K7elAoGATPr/1E7UQGGFWRb3WN2QnuKi +uBCFNOCTGQ9JzvE7m9Q9s+AXr7slVbruCDX4ev4Efs66NUhnNHLdPKbgtJXUFNoD +W/02Tiow+iL41qDUXzIJ5lgdq782ScWKafFjLrdCk+MGmFyWllmDucfCnpsljzte +aDOWO9QdUwdE2KRjV3MCgYAyZK5+LbDxYCJZF69gopEs7sLHJlbWJCDRitQCXxXM +9gS7KUjGbhq3P5o8G9VBe2KUCxikLgwjxJ+ogKUEW2hmXD3kCHW6DuNf2XDPjiLF +ZWde3aRbqMeqJgNxIUyeI70uLmP7mabanr4PhIYvLomKYmi7Ybg9lNgaa5AUqE0R +9wKBgB8LYjinisX8pGaSfGpxEHQu/giH9SqUyXoDyk/SkqmhmayB341P+x3Zdlrx +xBVYsxdQRbxbiO2CdihPDbiT4wXS+p9mReOJeb/QluzGpPuloSkx87uW1kcErZ/m +vr8opZ6ITlSSQeigwabVpkthDzNgQbEeHblbX0F9io56vX8w +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u1.pub b/t/keys/u1.pub new file mode 100644 index 0000000..3838e9a --- /dev/null +++ b/t/keys/u1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAnqiW8xCSfg/JO7acBvarVghAyM2Xw2ZzwaQN5xOg/XOSt2wV5wsT32hmPg/v8NqXcjKXMyI76ZO4U9To9yDvs6xKC9NwFmV6prjf6IC2gryykg2LBvWKIrcr/tNBnKgDAs7SDtf1EEN5Byf0mTcy9c9eVMsxvwm3oXqXKCm1YkKeaWOEd0xuRMRAVHlfon4M67r/heDZho9QLwvyDhorefhoLGOzrUbsXbcTHAEkt+m56swz7d7yZVSXckqrQ+KyY6cVjm1kdBvO7B34TpjMYubv4a3WPC98QUAFSRir3+HW43JFU/ty4Z7pCbpDBPctvg1fdCzIx1sg54zE6MZGnQ== tester@sita-wd.atc.tcs.com diff --git a/t/keys/u2 b/t/keys/u2 new file mode 100644 index 0000000..f259739 --- /dev/null +++ b/t/keys/u2 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA9NNn9rpF3+1reKJxfqQcjmugWkvN4VZfi6Uln1q6RJlohh6G +FhSTbA+puGjWy03VROa2K3TUxKKtPHMCFIOVpnNN3e4kAJbW1P/DbeYL5/kYo0tc +uNexFch2/4FrcykBoxZvTGUH1hJNIANjcAs+u+rzbwyiH5iQKIukfe7de2LrJEyw +/n05yAkMVfc2E97ge6X7rwGOpKaqMyy5S5Wjr6YxD54CAybdys9XWbwZYFx9s14j +PFK0frtXE7qFmR+hch9rgEro3jvIqLkQpO2u/ZfcoHllRDTqXZVGRGfrfZ8UHYsn +32+iApLFFdKcPkTtJokmq++b61NoQVQfk54yvQIBIwKCAQBM8fwaV8zRWT8IqBUK +i1lultqgCTITns2SSdifzA6n2HFOuSLTvVLncqMOElIio27pxNoq6jQ9zLoaUAf3 +0ZVu08gD68IsEiZC8UwMmMHp0fHM68yvHzenwqkOeSHFG4Qr8RuqS6NgiWiySjUx +16YAi6uX7gcRPpO0+LAKUmLdoiGLjF/bpMAyhfvB/EzAQESoscEhGywwboJfMJGK +FlKdazqYlYy4txjw53bUbuN/lsQYToAbe68zIVTBkKVZlBrBeOp1FPPoNXtaOuWx +tXsaiQIBXbJY9AaI9wCJsK9pRU92k23gyBENNHC/hPvyK0JWpkRhd9rsl79BG/xh +dJ6LAoGBAP6RSyKtjbwb6tuZWMpGeY87WnM2JXmuNRwR2n3KnM5VOsgLgyTKx11Y +9+B51b0yx3hVKJ6xbs4pjq/MLgAGXgfzh0VBJUjxiVKfHc0qhrUg1KAmGbhczYkE +69/oQP7d0vsgC7gR3xr0QMI8v9tZ9UDsPrzqOKZ4162YlNAmOv19AoGBAPY0FF1Q +QF/0FsnfB0kMmNPhuNwmy3EWhkxmzvgPa/0Ic1F7s2NJu9P+9vzHnM4jQPKFO75h +/USD8SU8wEjqGOdFi8fh4vTyk+2Na3/n37f8sjYY77tAvAa8Ix7Ul6c5knk+ZIaN +/K3kThB3sXCzOmTJfzMXOZn2DULZlM9Okw5BAoGBAKdJiSVqvDnJM+9kxVG5SJFS +46rLz3vY4RJyId2iZws/UoN1R400zCdmWb9mAXxUkbV5yjxl+FuXpuiNfVB5NnpA +8n33nCieuVONIjZdxjyDSeWVYVvlNpveqaHEgnuZFZ21+RKPZrnw91sR+muS4v7H +IenbvtPLwO5rlP3P+uEXAoGBAOgieZnPUqOc/4sivbnnTkQd93B8WWqgNXs8XL3/ +6XmLn+29xmTtv7lA2j7K1axqYc64ImML7suSWGTwIwLrWU8Vr63U+o8t2+6bSBmC +2j/CXuKMikLWoqdDrBXBIUXl0z8dkfs1apVTmg+Gy99J5pmKyGNfCmyXiNihQync +XsRLAoGBAN50w7bf+lGJ0KDoMIAGRqLJpm0NLd980R7j+yR49g8N/RjKrXz8zFuo +7NJztYXT1oBNv9HuO7Aq0z7gGQiNbjarGVgblqZi3RN7IE8/Cs4Ev50BLVWM1WTN +x4yFgK1OC3FfJaY6rn8u9y0dygTFvKrQx68gO+RgAIb4t/ZUoySW +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u2.pub b/t/keys/u2.pub new file mode 100644 index 0000000..11fa108 --- /dev/null +++ b/t/keys/u2.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9NNn9rpF3+1reKJxfqQcjmugWkvN4VZfi6Uln1q6RJlohh6GFhSTbA+puGjWy03VROa2K3TUxKKtPHMCFIOVpnNN3e4kAJbW1P/DbeYL5/kYo0tcuNexFch2/4FrcykBoxZvTGUH1hJNIANjcAs+u+rzbwyiH5iQKIukfe7de2LrJEyw/n05yAkMVfc2E97ge6X7rwGOpKaqMyy5S5Wjr6YxD54CAybdys9XWbwZYFx9s14jPFK0frtXE7qFmR+hch9rgEro3jvIqLkQpO2u/ZfcoHllRDTqXZVGRGfrfZ8UHYsn32+iApLFFdKcPkTtJokmq++b61NoQVQfk54yvQ== tester@sita-wd.atc.tcs.com diff --git a/t/keys/u3 b/t/keys/u3 new file mode 100644 index 0000000..561bbc8 --- /dev/null +++ b/t/keys/u3 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAs7YceedkXYoPDinUWKISRv48DbcAdIgw+bm/EBwsHcBHxr8L +bX6jNTRQifp7aiCNeoW7U+sqDsJ9r4CqDWagXg/o2uhYDbdizz3Gpiwslqi0tvu+ +e3/0J327wTB81v2pZ8j6DILR+kN+S8LXUHzUsFus8XHK3SvyWqlxqRBOpkP3tqB5 +Grfjuwn9IJJcvqcqkjXJcDuij9RE7EHmWGEv6c4MtS5mTpXGy9xPnehwcGzm5fEi +sxzLts/F6YgC6p7D7ZxR0Hm6YQteIFjhFkIMqNjPu78jrEfWD84C+wmpypSjVFMW +QaH+qDeeDWRE6WUufH+Uirzm7DLvHdnsgCeGoQIBIwKCAQBhjr8AWQqZLbBmxkAS +26OFn/S2/PGeWI+dghdDQn5ZSx+mZ7W/GNxBdCu4nes5nKSS+CPdGUK3jib433JB +yf9JAVKFdtC2/ScCycOcCVoIpLnfrTuE3w+DJv+GIaLbG/zgkaxP7z7Jr5xVA17x +LdKLn3sj+/HIhZIxN2mWSq5aQT4GElmiyXTcOLr/WyFZwhlwCAWPFlSxnqUEq+fi +qgPOxmI+w4a+GnrulqDYEpGvY8g0Z87yJHr8oROOhPRVWp/km3fodA4gBanwzK7P +8bvYtcMxOdK6cD85zgGbuaI+IB0jP9NQijBr6a8I8Mxj2VUHk9VtGrv+HcUZ39g/ +V99PAoGBAOTWbNbEmcgfJutlqEBgbl36g4hkpYazSQZlcp1IkHP4V9kk1+Zyibsq +9HtQ4JCT0CEJ7pdXNnfeRbG1jgnovWTqFGxHyPDqwYtHuQ6qMKLVQA/qqBxbp4AN +sPQqy2SdMPcr5Hb9di3WX/ztlMiZlk8Nd73DSQXum0+2j3PEuxO3AoGBAMkK6WkB +02zC2OGuoI4XMZBClgDpDqhEgfOZJtPMl4yCCMH0TKKYzcrb7U2xh1o7Nc5TDWx1 +bTtlfE5+SGzQcFNr4sVIRspjuKdFd53figJR3iJbdp4UImhGyf5gif9cocdTSeQb +mQvVKSjL+PZUgeR8BPzIk3k29P5thbR8oihnAoGBAJzqzkogaXNIj7dbppKLRF2z +GF2G1+dWXfXB6DFWVGzHjLIn5IDDkaTqQT6d29DaYtTTnEqE8iZPRbuvsdrovGJ0 +oEo/2j7M3HzDd5UG/MdtqEVttRrCgXxhKOHxdYbSMDR13n16mi4PV8NhFZDeWHC4 +xyMJkSijnbMA12VTs3s7AoGBAInbmL0IkPofNaIRWCbrVS+4oV+1wOhpfa5aY8Rv +CNVgewinhQHH3ZJq692BDFshSXeJaEpfJlSdXI2YbC1burzcQ7p6tDMCjT+AF4Iy +4kq7y4VGCDHh67U32veSp8UMUa1AbS6z1qkHMiqaftTMO8/gA2uCOT0s/8RoW672 +YJC7AoGAbpYmmhMp/hITKqLxFmzN+mK0lt8Ulgqo+xhoPycRXGJ0LcQntwL28Bvv +gKU4L0YockExwedM4Xjgvos0bj8vtAmFsVqWTiVOMIr1hMwPj79cyMgOILO5fJ2T +2s2dafFQiysUddn9RlDpcm18/Nn3CYOc5ZZvMtqMWe8nFwjklLE= +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u3.pub b/t/keys/u3.pub new file mode 100644 index 0000000..a0ecdf6 --- /dev/null +++ b/t/keys/u3.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAs7YceedkXYoPDinUWKISRv48DbcAdIgw+bm/EBwsHcBHxr8LbX6jNTRQifp7aiCNeoW7U+sqDsJ9r4CqDWagXg/o2uhYDbdizz3Gpiwslqi0tvu+e3/0J327wTB81v2pZ8j6DILR+kN+S8LXUHzUsFus8XHK3SvyWqlxqRBOpkP3tqB5Grfjuwn9IJJcvqcqkjXJcDuij9RE7EHmWGEv6c4MtS5mTpXGy9xPnehwcGzm5fEisxzLts/F6YgC6p7D7ZxR0Hm6YQteIFjhFkIMqNjPu78jrEfWD84C+wmpypSjVFMWQaH+qDeeDWRE6WUufH+Uirzm7DLvHdnsgCeGoQ== tester@sita-wd.atc.tcs.com diff --git a/t/keys/u4 b/t/keys/u4 new file mode 100644 index 0000000..be1b027 --- /dev/null +++ b/t/keys/u4 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAyjyPl0ARSBYZTuZtkPJH+3wqruonGvchQpR+RgUjJKbPJqgv +dbuqaiTUMHnEDzTPWM54IBP78OdGntcDxhUCgZKC9k2IZg0aT/EvwcavZswBrw/3 +GSJ22pr5VR/zBLoLBK1oQedJY1rMIo3LeqreyRo7gFnvSGX1s2VsmkrLhz687HM+ +64GII0RtoFj2wZ2ZL5ssT++Nt8mLypdwOHEiIZjtrqK5UtEvUrVvGVN0vICyqB/1 +mftzo9zjEj3BoBIQieOKLZOraHgsvUwdpYD52ASD3kAS5tiJbvpG9D81E5Zkfk4P +xDaKeRF5IFivxbM2MCztcGtJi5hRw9eU0evXrQIBIwKCAQAXHN0122+wd43dIaYf +MaHTmSlzE3Iu96wHm/Eskt9x5y2eBJe1rwwpY0tzQR24mFI2CPfJJtr29dwvaQe+ +3dRmlGa9ECzYdoaqDO+D3DFNk6hrx09wljl4EbYYW2w7DfKhcudX/Sz1aXZqWVkV +VVtKLuI6je91/QYjIYjBLR6THOiXZRePbzY2/zMktm35v625b5k3CS1BKbd8jAfY +zUf70ceOgFZlLT55ab1tAEQaMFI/UhJTl9oTKnnZF9ULc0HBqHo6/pbaxZ1rrlb7 +D0H/Wbqf/INtdpMI32OVRSdtyYo9GezBBLc1VkcSmA+o2eOUQe6jldt71welXSFX +fsPbAoGBAP2Xql6WyGvKVzksZOHL1Huh0x01UPSIdSIjnA5zQe1euZbCKUZDN939 +xPM8Xtno4nSMtLF6wbFS8iWJI1Ecp121rzG5dNSIvFYNjaXma44PlU70smrfGPTt +v2LKeXKiaZAaPo8b+qwhdfqgGyuCRqSsD6d5dEf3skGf6rRnoyRXAoGBAMwoFFIt +rAH3tDTyt9Kse+13N5J5Dk01J2QlMh0cKv7nc8TNln8aTW6uk0JjMkbud25xXSPD +yQ4pNxNdiehfOjN1sirv6y7lerVBiP6qT+pOHhPxrCoJ8g3zmDzPqKlwiVrNbKkP +UptE6JCdivs9+goy3F3fl8EYTI08/fyss4GbAoGBAKalfpXuDq070usOi2/PFppj +BxMxqjpLCyUQFhgf2t3QiJZFE88WOqfSpf7tKGNP5UVGhV6vwRzBeo2x5AIaJNcu +P+153w9SimRvTnRVlyLXClh06kY61eLIHq6iT879A4qU50gZs1sr7nF/J8wiaO/e +yHVecPwXzOlL1L+x0ZuJAoGARf8c6Pm3USG6IMg/BmcF6wRNkU4TiC97OEdEYcCD +xRwnsTCLXscwfbgyfSlTEQFNhPOq57DXOA4hgvt/vWJ6WslEZn4kv58iwc1TioOJ +HSIY8OUlFbpEXI4IT10j4lJ4PGOwOfaf+riKQDYDw9q8IMD7GN7yM5NNckDMHB30 +ZvMCgYEAstjuwtdD5qNQaGL4dCplVHmfBohXpZayhAQN3ziY/yScIziM9uBCGcKv +E8toH0EgJBp06+dWZ/2mzgByEhlqKwfbGd1Z33ed3zzQNl7NAMzqKHOtN7KPeNLx +5mzEOwryf9WqPGpjAnFyIYF/ZTN9JgHWUqTI1jVW4lefOw2Ji8w= +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u4.pub b/t/keys/u4.pub new file mode 100644 index 0000000..36ef4f1 --- /dev/null +++ b/t/keys/u4.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAyjyPl0ARSBYZTuZtkPJH+3wqruonGvchQpR+RgUjJKbPJqgvdbuqaiTUMHnEDzTPWM54IBP78OdGntcDxhUCgZKC9k2IZg0aT/EvwcavZswBrw/3GSJ22pr5VR/zBLoLBK1oQedJY1rMIo3LeqreyRo7gFnvSGX1s2VsmkrLhz687HM+64GII0RtoFj2wZ2ZL5ssT++Nt8mLypdwOHEiIZjtrqK5UtEvUrVvGVN0vICyqB/1mftzo9zjEj3BoBIQieOKLZOraHgsvUwdpYD52ASD3kAS5tiJbvpG9D81E5Zkfk4PxDaKeRF5IFivxbM2MCztcGtJi5hRw9eU0evXrQ== tester@sita-wd.atc.tcs.com diff --git a/t/keys/u5 b/t/keys/u5 new file mode 100644 index 0000000..f525303 --- /dev/null +++ b/t/keys/u5 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEAonPym8U2ElyMBPVtk5rLqz44QG9pSTTpOj8AmqBRpy9W0B6C +4yuYf4oVNJMD3GEq6Y65uhmSmN/Nqi99++TtwrNPCRYwXHHa2ZUwHT6ow6Izkrnn +nzkkPBgpBOKmn/M8z7xlsQhmEBuKlUgYOrdnfRnkvOTTuF4Fq4jSpagOnLjpPlxr +KZ6vcdvVjZ3kWtZEI+HowS1IKxZsI0UbP/FzSUFrlxLAedrPFBUiUfdl/VPOMs7t +AjHFUMepq4vzjQlupuJEhaB24nCQFlV5gLQjrGgRs3gs6AJRCYMtE5edsllG1hJV +ro2UGiH8pvOOso4LYKbyiHuUjuToRlUNjpqRqQIBIwKCAQBTjBZevTHATNpLsWuN +vVLNGK83FLnN4LJ1ugBPhaZV+xa0LPLbOv34cuZVjW+zKqha26/5TvrviQqvS5/g +oZ7ZKQQTTT1xXyAYINbqd/8Fhp4uM7jyzO4QRvCGK2uit5ueCR5bC6I0K2vXvq1g +Boz+glhhJTmvRkwPEyqBFJnM8JBAxp57WEjC0tK6Cw8soKEgfwe6dUouRRdEA6q7 ++Z86azz8YkenUM8o/j6IRshJGBBpOfF1c6NY1M9CLebSKIocg9jxBqdMZHnzpqSW +qxjibSUGQWU/7o45TRAefdpQR6+HLR1mGxvxtU94/AH94MjmUv827Nd6OhMyynh5 +s0cjAoGBAMwm96LBouDUsmhThbK27a3B0CnlkS1FYkwuNbudWoS+kGPktu3S/rqe +i8Czn6mQEKaEvY0ECcnPaSN9Nno7G26TrrUGjIAjWFTKWJ4HognuLj0eBf+OLfrg +WgcXZJtK4vk8hY26DsleIO8DEg/Vkg0x9EozEI4Qbg3IRbvroiHzAoGBAMu14pQ/ +QevNAUVCHyJbWutNjIUp8O42ZqubegVt+Kq1iy2OuSrhhg/et3ZABv4YBiQ2y1/S +qq+p+Z3YUY3Vu54xFr7/v+pVOGqc437+xWlbSSkJSFpBErNkaKMWPkuu6uoStQ7K +ZoYtIIFVE+zEqneLSincZZxmIl/UMErvOEjzAoGAF1TpGeo+gBhO51oAprXSBTq4 +tFTAIm5UYHpPSKRE2/iFh8JeDI0kbRlvDrVxVTUJN5okSqFgNFI35sx7QStTl5vZ +c8Y78WMgCbC5q6kZ1T/KxSgAr332oFQni8gowUpjFSt8+kEmQuYvpkl+aDWqSqaf +k3OM3QkxJiWET/2sISMCgYBLqfUSfeVI9F+OwMm8TdFQFX1WCEOLrc5c/zvzY1xc +qdSb5I3rWxSJjTzUJmj/SsBz2daCrVVXMIFJQbfkOXGSh0L2fD/388R/Xtgn2vjd +/VWwKATuUq8ssEQfUWeYdCteQXZzNTwF84EaGEk6r3KELGwA63YyxtmRTtAb1TIp +uQKBgQC60e8R2YqZ6ylmPygjOAMw3rsEyDYrEtMqIKZIgrjAvEnrhyif9LS2a1aF +6Hg3IJmeTVt6ql5iDYK3nfnv/rSLs2vAdGwD082Lasx/bk8kDro0fSVylI50g1/U +BRzkoHBZb6mKKJfFNzDr0Py790lmsp3DYIYPxoouvSgDvcKJZw== +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u5.pub b/t/keys/u5.pub new file mode 100644 index 0000000..95b8d39 --- /dev/null +++ b/t/keys/u5.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAonPym8U2ElyMBPVtk5rLqz44QG9pSTTpOj8AmqBRpy9W0B6C4yuYf4oVNJMD3GEq6Y65uhmSmN/Nqi99++TtwrNPCRYwXHHa2ZUwHT6ow6IzkrnnnzkkPBgpBOKmn/M8z7xlsQhmEBuKlUgYOrdnfRnkvOTTuF4Fq4jSpagOnLjpPlxrKZ6vcdvVjZ3kWtZEI+HowS1IKxZsI0UbP/FzSUFrlxLAedrPFBUiUfdl/VPOMs7tAjHFUMepq4vzjQlupuJEhaB24nCQFlV5gLQjrGgRs3gs6AJRCYMtE5edsllG1hJVro2UGiH8pvOOso4LYKbyiHuUjuToRlUNjpqRqQ== tester@sita-wd.atc.tcs.com diff --git a/t/keys/u6 b/t/keys/u6 new file mode 100644 index 0000000..8896e8a --- /dev/null +++ b/t/keys/u6 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEApqmFYqqjj1pPRGICRIIYLTF9JoLz+oU5nf+jei7WVxlE4Z7T +AtUn/8ldZiJb8+OKtX0h/P6cuxcs3ZclAeICC5DYZ+zfgb0blIrz3n7x4EC8Ti/w +PhIL3OjmoUIl3PqvxE9QmwIMG5TfIIOkvGBZAGB2YiyusohLqqJ26m/SY29yzpf8 +zqu2aJLV+rYeDPGgkOFVFaTAYxTWDUZAqFEuBZm1Nhs413dOa2oBDT+DY/TnY7D0 +TDbhai3/nX1v0Mx3F96aGKxPfQsiQs8bniAZa+F5NO5F67avq0YtKF24JmP8zCm1 +WQ2I/CqjtxbBKJNPKZAE25VnJi7+BRStFAwXIwIBIwKCAQEAihduhPPJWYVXnw9g +9u9zHiGw1sRGfx3t6VAoXexLMjmCN0kVPNyAOlZjVKAgTbyI34TwRqcUJf1CbnXr +dpaxPMh4yyNaGwpnTzFNuFqN5bH7HDZZWABS+N47b6vWOsEVSuKh34VafUDWIkEw +uVclLDKrO2bZ5GJNUt5ih7u880xgibWN6sm6UdO5SL/Y+0imWwipGDFERBBexBT9 +xZwFTilSb9Eo+WxfLJQhqRnbhPQvH107oId4v5oxw5FvQSM7JSF2hyTLbz7pXMIb +sjolhxIikhok3S991xPRl62Q38rl6RitzsrN1/UVl24qN0CUmu3qGqO7YRix4m1h +K3FS6wKBgQDSpqRGEy41eUlmwNhXz8K0pSJkYB3j3OgaxlorDebrE5GVT0o4SKaB +qyx4z0XjARrrQxU91OoCtotsdIkjYtKArQzgqv4Wrc6MoulTdpmSCxGtwxgvSTSv +YOEsywDomS6c/hwziTciRyPAD6eUxOle93qmX2lMsvMNcwiHoYn60QKBgQDKipQu +w2+OaThAKK+dDejlWpEtdQTH7WhsNvYPdvSU0X36M7oVuhA2k0Qh0v26wmPU6RmI +/C3IjpkHptrXz+ns1BNE3DsxoUAa4H/tWUBaFhIu9ni+AX2mNSnFnXmt8d9kzzZX +xJsEWBzkE/ve9NeVHtLrq3gmpcXcFteVsNYHswKBgAwJhbrchkwy0P6OrUbYrApS +lEBAAbU/0r+zkB+3psuat9ylcfSWb+onCdsEh6aSWU9FqXE/XdRE8gYyi35dax1L +t5fPQbgnMF/Oy4EcuFF8+bIoZ8gwEaOfIs9cDq40o5PxQ3CowVJqdxJKCZN492SD +K5R6ewuydEnpUPHOt21rAoGAEVxV2B9hVVl52Z0WXes/2SUFISdJjXq/zsLh5BGC +rauOdIgXQ7Dc0XpWTArFSoWwyRtLVOJqUwToi6CWakUFbBItiY84RhUiwHmkkK/g +8cdgpPCOAahbOiHQYWVE8acEhPvRdTy81HyUwxen3+kZyvQDc0k2LzLH0QlFrb6z +Qn0CgYEAmQqc19g5dEpHfbdOPeSrgdf3urcyt6ot1jL9i6B2U6akfvp5GFhGoAs/ +C6q6mplU0by/bmMtYyRhY+7tux2Wz5yaMDwUU5/jnECtt8W7gDi5Y6vaRTIc/2n/ +j53jeI8PJgKNHeF+tbILdUUHDsTLvx/PWVkae1SzDRY8swbIxyI= +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u6.pub b/t/keys/u6.pub new file mode 100644 index 0000000..d0b5fbb --- /dev/null +++ b/t/keys/u6.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEApqmFYqqjj1pPRGICRIIYLTF9JoLz+oU5nf+jei7WVxlE4Z7TAtUn/8ldZiJb8+OKtX0h/P6cuxcs3ZclAeICC5DYZ+zfgb0blIrz3n7x4EC8Ti/wPhIL3OjmoUIl3PqvxE9QmwIMG5TfIIOkvGBZAGB2YiyusohLqqJ26m/SY29yzpf8zqu2aJLV+rYeDPGgkOFVFaTAYxTWDUZAqFEuBZm1Nhs413dOa2oBDT+DY/TnY7D0TDbhai3/nX1v0Mx3F96aGKxPfQsiQs8bniAZa+F5NO5F67avq0YtKF24JmP8zCm1WQ2I/CqjtxbBKJNPKZAE25VnJi7+BRStFAwXIw== tester@sita-wd.atc.tcs.com diff --git a/t/out/t01-repo-groups.1 b/t/out/t01-repo-groups.1 new file mode 100644 index 0000000..1cf2e03 --- /dev/null +++ b/t/out/t01-repo-groups.1 @@ -0,0 +1,99 @@ +$data_version = '1.5'; +%repos = ( + 'aa' => { + 'R' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'W' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'u1' => [ + [ + 2, + 'refs/.*', + 'RW+' + ] + ], + 'u2' => [ + [ + 4, + 'refs/.*', + 'RW' + ] + ], + 'u3' => [ + [ + 5, + 'refs/.*', + 'RW' + ] + ] + }, + 'bb' => { + 'R' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'W' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'u1' => [ + [ + 3, + 'refs/.*', + 'RW+' + ] + ], + 'u2' => [ + [ + 6, + 'refs/.*', + 'RW' + ] + ], + 'u3' => [ + [ + 7, + 'refs/.*', + 'RW' + ] + ] + }, + 'gitolite-admin' => { + 'R' => { + 'tester' => 1 + }, + 'W' => { + 'tester' => 1 + }, + 'tester' => [ + [ + 0, + 'refs/.*', + 'RW+' + ] + ] + }, + 'testing' => { + '@all' => [ + [ + 1, + 'refs/.*', + 'RW+' + ] + ], + 'R' => { + '@all' => 1 + }, + 'W' => { + '@all' => 1 + } + } +); diff --git a/t/out/t01-repo-groups.2 b/t/out/t01-repo-groups.2 new file mode 100644 index 0000000..9a321b0 --- /dev/null +++ b/t/out/t01-repo-groups.2 @@ -0,0 +1,63 @@ +$data_version = '1.5'; +%repos = ( + '@g1' => { + '@g1' => [ + [ + 2, + 'refs/.*', + 'RW+' + ] + ], + '@g2' => [ + [ + 3, + 'refs/.*', + 'RW' + ] + ], + 'R' => { + '@g1' => 1, + '@g2' => 1 + }, + 'W' => { + '@g1' => 1, + '@g2' => 1 + } + }, + 'gitolite-admin' => { + 'R' => { + 'tester' => 1 + }, + 'W' => { + 'tester' => 1 + }, + 'tester' => [ + [ + 0, + 'refs/.*', + 'RW+' + ] + ] + }, + 'testing' => { + '@all' => [ + [ + 1, + 'refs/.*', + 'RW+' + ] + ], + 'R' => { + '@all' => 1 + }, + 'W' => { + '@all' => 1 + } + } +); +%groups = ( + '@g1' => { + 'aa' => 'master', + 'bb' => 'master' + } +); diff --git a/t/out/t02-user-groups.1 b/t/out/t02-user-groups.1 new file mode 100644 index 0000000..f5067a8 --- /dev/null +++ b/t/out/t02-user-groups.1 @@ -0,0 +1,66 @@ +$data_version = '1.5'; +%repos = ( + 'aa' => { + 'R' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'W' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'u1' => [ + [ + 2, + 'refs/.*', + 'RW+' + ] + ], + 'u2' => [ + [ + 3, + 'refs/.*', + 'RW' + ] + ], + 'u3' => [ + [ + 4, + 'refs/.*', + 'RW' + ] + ] + }, + 'gitolite-admin' => { + 'R' => { + 'tester' => 1 + }, + 'W' => { + 'tester' => 1 + }, + 'tester' => [ + [ + 0, + 'refs/.*', + 'RW+' + ] + ] + }, + 'testing' => { + '@all' => [ + [ + 1, + 'refs/.*', + 'RW+' + ] + ], + 'R' => { + '@all' => 1 + }, + 'W' => { + '@all' => 1 + } + } +); diff --git a/t/out/t02-user-groups.2 b/t/out/t02-user-groups.2 new file mode 100644 index 0000000..63ab262 --- /dev/null +++ b/t/out/t02-user-groups.2 @@ -0,0 +1,71 @@ +$data_version = '1.5'; +%repos = ( + 'aa' => { + '@g1' => [ + [ + 2, + 'refs/.*', + 'RW+' + ] + ], + '@g2' => [ + [ + 3, + 'refs/.*', + 'RW' + ] + ], + 'R' => { + '@g1' => 1, + '@g2' => 1 + }, + 'W' => { + '@g1' => 1, + '@g2' => 1 + } + }, + 'gitolite-admin' => { + 'R' => { + 'tester' => 1 + }, + 'W' => { + 'tester' => 1 + }, + 'tester' => [ + [ + 0, + 'refs/.*', + 'RW+' + ] + ] + }, + 'testing' => { + '@all' => [ + [ + 1, + 'refs/.*', + 'RW+' + ] + ], + 'R' => { + '@all' => 1 + }, + 'W' => { + '@all' => 1 + } + } +); +%groups = ( + '@g1' => { + 'u1' => 'master' + }, + '@g2' => { + 'u2' => 'master', + 'u3' => 'master' + }, + '@g3' => { + 'u4' => 'master', + 'u5' => 'master', + 'u6' => 'master' + } +); diff --git a/t/out/t04-wild1.1 b/t/out/t04-wild1.1 new file mode 100644 index 0000000..c82ca2b --- /dev/null +++ b/t/out/t04-wild1.1 @@ -0,0 +1,5 @@ +New perms are: + +R u5 +RW u6 + diff --git a/t/out/t04-wild1.2 b/t/out/t04-wild1.2 new file mode 100644 index 0000000..3b769ae --- /dev/null +++ b/t/out/t04-wild1.2 @@ -0,0 +1,4 @@ + +R u5 +RW u6 + diff --git a/t/rollback b/t/rollback new file mode 100755 index 0000000..4fdb260 --- /dev/null +++ b/t/rollback @@ -0,0 +1,11 @@ +#!/bin/bash + +export TESTDIR=$PWD + +cd +rm -rf gitolite-admin td +tar xf rollback.tar +mkdir td + +scp $TESTDIR/rollback.server gitolite-test@localhost:rollback +ssh gitolite-test@localhost ./rollback diff --git a/t/rollback.server b/t/rollback.server new file mode 100755 index 0000000..b9c7e45 --- /dev/null +++ b/t/rollback.server @@ -0,0 +1,5 @@ +#!/bin/bash + +cd +rm -rf .ssh .gitolite .gitolite.rc repositories gitolite-install +tar xf rollback.tar diff --git a/t/t-fedora-big-config b/t/t-fedora-big-config new file mode 100644 index 0000000..6604bd0 --- /dev/null +++ b/t/t-fedora-big-config @@ -0,0 +1,24 @@ +# vim: syn=sh: +cd $TESTDIR +$TESTDIR/rollback +editrc GL_BIG_CONFIG 1 +editrc GL_NO_DAEMON_NO_GITWEB 1 +catrc +expect "GL_BIG_CONFIG *= *1;" + +# ---------- + +name "first round" +date | hl +cat data/fedora.conf | ugc +date | hl + +time sync + +name "second round" +date | hl +echo " +repo foo +RW+ = u1 u2 +" | ugc +date | hl diff --git a/t/t00-initial b/t/t00-initial new file mode 100644 index 0000000..7ff8157 --- /dev/null +++ b/t/t00-initial @@ -0,0 +1,70 @@ +# vim: syn=sh: +$TESTDIR/rollback + +# ---------- +name "basic push admin repo" +echo " + repo aa + RW+ = u1 + RW = u2 u3 +" | ugc + +expect "To gitolite:gitolite-admin" +expect "master -> master" + +name "basic create repo" +expect "remote: Initialized empty Git repository in /home/gitolite-test/repositories/aa.git/" + +# ---------- +name "basic clone" +cd ~/td +runlocal git clone u1:aa u1aa +expect "Initialized empty Git repository in /home/tester/td/u1aa/.git/" +expect "warning: You appear to have cloned an empty repository" +runlocal ls -ald u1aa +expect "drwxr-xr-x 3 $USER $USER 4096 201.-..-.. ..:.. u1aa" + +# ---------- +name "basic clone deny" +cd ~/td +runlocal git clone u4:aa u4aa +expect "R access for aa DENIED to u4" +runlocal ls -ald u4aa +expect "ls: cannot access u4aa: No such file or directory" + +# ---------- +name "basic push" +cd ~/td/u1aa +mdc +runlocal git push origin HEAD +expect "To u1:aa" +expect "\[new branch\] *HEAD -> master" + +# ---------- +name "basic rewind" +cd ~/td/u1aa +mdc +mdc +mdc +runlocal git push origin HEAD +runlocal git reset --hard HEAD^ +mdc +runlocal git push -f origin HEAD +expect "+ .* HEAD -> master (forced update)" +name "basic rewind log" +taillog +expect "\+.*aa.refs/heads/master.u1.refs/.\*" + +# ---------- +name "basic rewind deny" +cd ~/td +runlocal git clone u2:aa u2aa +cd ~/td/u2aa +mdc +mdc +mdc +runlocal git push origin HEAD +runlocal git reset --hard HEAD^ +mdc +runlocal git push -f origin HEAD +expect "remote: + refs/heads/master aa u2 DENIED by fallthru" diff --git a/t/t01-repo-groups b/t/t01-repo-groups new file mode 100644 index 0000000..d879894 --- /dev/null +++ b/t/t01-repo-groups @@ -0,0 +1,58 @@ +# vim: syn=sh: +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 0 + +name "base output with no groups" +echo " + repo aa bb + RW+ = u1 + RW = u2 u3 +" | ugc + +catconf +expect_filesame $TESTDIR/out/t01-repo-groups.1 + +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 0 + +name "output with eqvt repo groups" +echo " + @g1 = aa bb + repo @g1 + RW+ = u1 + RW = u2 u3 +" | ugc + +catconf +expect_filesame $TESTDIR/out/t01-repo-groups.1 + +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 1 + +name "base output with no groups but GL_BIG_CONFIG on" +echo " + repo aa bb + RW+ = u1 + RW = u2 u3 +" | ugc + +catconf +expect_filesame $TESTDIR/out/t01-repo-groups.1 + +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 1 + +name "repo groups with GL_BIG_CONFIG on" +echo " + @g1 = aa bb + repo @g1 + RW+ = @g1 + RW = @g2 +" | ugc + +catconf +expect_filesame $TESTDIR/out/t01-repo-groups.2 diff --git a/t/t02-user-groups b/t/t02-user-groups new file mode 100644 index 0000000..29d98ce --- /dev/null +++ b/t/t02-user-groups @@ -0,0 +1,64 @@ +# vim: syn=sh: +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 0 + +name "base output with no groups" +echo " + repo aa + RW+ = u1 + RW = u2 u3 +" | ugc + +catconf +expect_filesame $TESTDIR/out/t02-user-groups.1 + +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 0 + +name "output with eqvt user groups" +echo " + @g1 = u1 + @g2 = u2 u3 + @g3 = u4 u5 u6 + + repo aa + RW+ = @g1 + RW = @g2 +" | ugc + +catconf +expect_filesame $TESTDIR/out/t02-user-groups.1 + +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 1 + +name "base output with no groups but GL_BIG_CONFIG on" +echo " + repo aa + RW+ = u1 + RW = u2 u3 +" | ugc + +catconf +expect_filesame $TESTDIR/out/t02-user-groups.1 + +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 1 + +name "user groups with GL_BIG_CONFIG on" +echo " + @g1 = u1 + @g2 = u2 u3 + @g3 = u4 u5 u6 + + repo aa + RW+ = @g1 + RW = @g2 +" | ugc + +catconf +expect_filesame $TESTDIR/out/t02-user-groups.2 diff --git a/t/t03-branch-permissions b/t/t03-branch-permissions new file mode 100644 index 0000000..c1766c1 --- /dev/null +++ b/t/t03-branch-permissions @@ -0,0 +1,20 @@ +# vim: syn=sh: +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 0 + +echo ===== testing with GL_BIG_CONFIG set to 0 ===== + +cd $TESTDIR +. ./t03a-branch-permissions +cd $TESTDIR + +# ---------- +$TESTDIR/rollback +editrc GL_BIG_CONFIG 1 + +echo ===== testing with GL_BIG_CONFIG set to 1 ===== + +cd $TESTDIR +. ./t03a-branch-permissions +cd $TESTDIR diff --git a/t/t03a-branch-permissions b/t/t03a-branch-permissions new file mode 100644 index 0000000..2d7c6e4 --- /dev/null +++ b/t/t03a-branch-permissions @@ -0,0 +1,177 @@ +# vim: syn=sh: +# ---------- +name "setup" +echo " + @g1 = u1 + @g2 = u2 + @g3 = u3 + repo aa + RW+ = @g1 + RW = @g2 + RW+ master = @g3 + RW master = u4 + - master = u5 + RW+ dev = u5 + RW = u5 +" | ugc + +# reasonably complex setup; we'll do everything from one repo though +cd ~/td +runlocal git clone u1:aa +cd ~/td/aa +mdc; mdc; mdc +mdc; mdc; mdc +mdc; mdc; mdc +name "INTERNAL" +runlocal git push origin HEAD +expect "To u1:aa" +expect "\* \[new branch\] HEAD -> master" + +git branch dev +git branch foo + +# u1 tries to rewind master; succeeds +name "u1 rewind master succeed" +runlocal git reset --hard HEAD^ +mdc +runlocal git push origin +master +expect "+ .* master -> master (forced update)" + +# u2 tries to rewind master; fails +name "u2 rewind master fail" +runlocal git reset --hard HEAD^ +mdc +runlocal git push u2:aa +master +expect "remote: + refs/heads/master aa u2 DENIED by fallthru" + +# u3 tries to rewind master; succeeds +name "u3 rewind master succeed" +runlocal git reset --hard HEAD^ +mdc +runlocal git push u3:aa +master +expect "+ .* master -> master (forced update)" + +# u4 tries to push master; succeeds +name "u4 push master succeed" +mdc +runlocal git push u4:aa master +expect "master -> master" + +# u4 then tries to rewind master; fails +name "u4 rewind master fail" +runlocal git reset --hard HEAD^ +runlocal git push u4:aa +master +expect "remote: + refs/heads/master aa u4 DENIED by fallthru" + +# u3 and u4 try to push the other 2 branches; fail +name "u3 and u4 / dev foo -- all 4 fail" +runlocal git push u3:aa dev +expect "remote: W refs/heads/dev aa u3 DENIED by fallthru" +runlocal git push u4:aa dev +expect "remote: W refs/heads/dev aa u4 DENIED by fallthru" +runlocal git push u3:aa foo +expect "remote: W refs/heads/foo aa u3 DENIED by fallthru" +runlocal git push u4:aa foo +expect "remote: W refs/heads/foo aa u4 DENIED by fallthru" + +# clean up for next set +runlocal git push -f origin master dev foo + +# u5 tries to push master; fails +name "u5 push master fail" +mdc +runlocal git push u5:aa master +expect "remote: W refs/heads/master u5 DENIED by refs/heads/master" + +# u5 tries to rewind dev; succeeds +name "u5 rewind dev succeed" +runlocal git push u5:aa +dev^:dev +expect "+ .* dev^ -> dev (forced update)" + +# u5 tries to rewind foo; fails +name "u5 rewind foo fail" +runlocal git push u5:aa +foo^:foo +expect "remote: + refs/heads/foo aa u5 DENIED by fallthru" + +# u5 tries to push foo; succeeds +name "INTERNAL" +runlocal git checkout foo +expect "Switched to branch 'foo'" + +name "u5 push foo succeed" +mdc +runlocal git push u5:aa foo +expect "foo -> foo" + +# u1 tries to delete branch dev +name "u1 delete branch dev succeed" +runlocal git push origin :dev +expect " - \[deleted\] *dev" + +# quietly push it back again +name "INTERNAL" +runlocal git push origin dev +expect " * \[new branch\] dev -> dev" + +# u1 tries to delete dev on a new setup +name "INTERNAL" +echo " + repo aa + RWD dev = u4 +" | ugc +expect "master -> master" + +# u1 tries to delete branch dev; fails +name "u1 delete branch dev fail" +runlocal git push origin :dev +expect "remote: D refs/heads/dev aa u1 DENIED by fallthru" + +# u4 tries to delete branch dev; succeeds +name "u4 delete branch dev succeed" +runlocal git push u4:aa :dev +expect " - \[deleted\] *dev" + +# ---------- + +name "INTERNAL" +echo " +repo r1 + RW refs/heads/v[0-9] = u1 + RW refs/heads = tester +" | ugc +notexpect ABORT +cd ~/td +runlocal git clone gitolite:r1 +expect "Initialized empty Git repository in /home/tester/td/r1/.git/" +cd ~/td/r1 +mdc; mdc; mdc +mdc; mdc; mdc +runlocal git push origin HEAD +expect "\* \[new branch\] HEAD -> master" +runlocal git branch v1 +runlocal git push origin v1 +name "no deny rules -- push v1 succeeds" +expect "\* \[new branch\] v1 -> v1" + +name "INTERNAL" +echo " +repo r2 + RW refs/heads/v[0-9] = u1 + - refs/heads/v[0-9] = tester + RW refs/heads = tester +" | ugc +notexpect ABORT +cd ~/td +runlocal git clone gitolite:r2 +expect "Initialized empty Git repository in /home/tester/td/r2/.git/" +cd ~/td/r2 +mdc; mdc; mdc +mdc; mdc; mdc +runlocal git push origin HEAD +expect "\* \[new branch\] HEAD -> master" +runlocal git branch v1 +runlocal git push origin v1 +name "deny rules -- push v1 fails" +expect "remote: W refs/heads/v1 tester DENIED by refs/heads/v\[0-9\]" + +name "INTERNAL" diff --git a/t/t04-wild b/t/t04-wild new file mode 100644 index 0000000..e7b5464 --- /dev/null +++ b/t/t04-wild @@ -0,0 +1,18 @@ +# vim: syn=sh: +# ---------- + +for i in 0 1 +do + for j in students all + do + hl t04-wild with GL_WILDREPOS $i, C=@$j + + cd $TESTDIR + $TESTDIR/rollback + editrc GL_BIG_CONFIG $i + editrc GL_WILDREPOS 1 + . ./t04a-wild-$j + + cd $TESTDIR + done +done diff --git a/t/t04a-wild-all b/t/t04a-wild-all new file mode 100644 index 0000000..ae9010f --- /dev/null +++ b/t/t04a-wild-all @@ -0,0 +1,144 @@ +# vim: syn=sh: +# ---------- + +name "INTERNAL" +echo " + @prof = u1 + @TAs = u2 u3 + @students = u4 u5 u6 + + repo foo/CREATOR/a[0-9][0-9] + C = @all + RW+ = CREATOR + RW = WRITERS @TAs + R = READERS @prof + +" | ugc +notexpect ABORT + +# reasonably complex setup; we'll do everything from one repo though +cd ~/td + +name "u1 create success" +runlocal git clone u1:foo/u1/a01 +expect "Initialized empty Git repository in /home/gitolite-test/repositories/foo/u1/a01.git/" +# expect "R access for foo/u1/a01 DENIED to u1" + +name "u2 create success" +runlocal git clone u2:foo/u2/a02 +expect "Initialized empty Git repository in /home/gitolite-test/repositories/foo/u2/a02.git/" +# expect "R access for foo/u2/a02 DENIED to u2" + +name "u4 tries to create u2 repo" +runlocal git clone u4:foo/u2/a12 +expect "R access for foo/u2/a12 DENIED to u4" + +name "line anchored regexes" +runlocal git clone u4:foo/u4/a1234 +expect "R access for foo/u4/a1234 DENIED to u4" + +name "u4 tries to create his own repo" +runlocal git clone u4:foo/u4/a12 +expect "Initialized empty Git repository in /home/gitolite-test/repositories/foo/u4/a12.git/" +expect "warning: You appear to have cloned an empty repository." + +name "u4 push success" +cd ~/td/a12 +mdc;mdc;mdc;mdc +runlocal git push origin master +expect "To u4:foo/u4/a12" +expect "\* \[new branch\] master -> master" + +name "u1 clone success" +cd ~/td +runlocal git clone u1:foo/u4/a12 u1a12 +expect "Initialized empty Git repository in /home/tester/td/u1a12/.git/" + +name "u1 push fail" +cd ~/td/u1a12 +mdc; mdc +runlocal git push +expect "W access for foo/u4/a12 DENIED to u1" + +name "u2 clone success" +cd ~/td +runlocal git clone u2:foo/u4/a12 u2a12 +expect "Initialized empty Git repository in /home/tester/td/u2a12/.git/" + +name "u2 push success" +cd ~/td/u2a12 +mdc; mdc +runlocal git push +expect "To u2:foo/u4/a12" +expect "master -> master" + +name "u2 rewind fail" +runlocal git push -f origin master^:master +expect "remote: + refs/heads/master foo/u4/a12 u2 DENIED by fallthru" +expect "remote: error: hook declined to update refs/heads/master" +expect "To u2:foo/u4/a12" +expect "\[remote rejected\] master^ -> master (hook declined)" +expect "error: failed to push some refs to 'u2:foo/u4/a12'" + +name INTERNAL +# u4 pull to sync up +cd ~/td/a12 +runlocal git pull +expect "Fast-forward" +expect "From u4:foo/u4/a12" +expect "master -> origin/master" + +name "u4 rewind success" +runlocal git reset --hard HEAD^ +runlocal git push -f +expect "To u4:foo/u4/a12" +expect "+ .* master -> master (forced update)" + +name "u5 clone fail" +cd ~/td +runlocal git clone u5:foo/u4/a12 u5a12 +expect "R access for foo/u4/a12 DENIED to u5" + +name "setperm" +echo " +R u5 +RW u6 +" | runlocal ssh u4 setperms foo/u4/a12 +expect_filesame $TESTDIR/out/t04-wild1.1 + +name "getperms" +runlocal ssh u4 getperms foo/u4/a12 +expect_filesame $TESTDIR/out/t04-wild1.2 + +name "u5 clone success" +cd ~/td +runlocal git clone u5:foo/u4/a12 u5a12 +expect "Initialized empty Git repository in /home/tester/td/u5a12/.git/" + +name "u5 push fail" +cd ~/td/u5a12 +mdc; mdc +runlocal git push +expect "W access for foo/u4/a12 DENIED to u5" + +name "u6 clone success" +cd ~/td +runlocal git clone u6:foo/u4/a12 u6a12 +expect "Initialized empty Git repository in /home/tester/td/u6a12/.git/" + +name "u6 push success" +cd ~/td/u6a12 +mdc; mdc +runlocal git push u6:foo/u4/a12 +expect "To u6:foo/u4/a12" +expect "master -> master" + +name "u6 rewind fail" +runlocal git push -f u6:foo/u4/a12 master^:master +expect "remote: + refs/heads/master foo/u4/a12 u6 DENIED by fallthru" +expect "remote: error: hook declined to update refs/heads/master" +expect "To u6:foo/u4/a12" +expect "\[remote rejected\] master^ -> master (hook declined)" +expect "error: failed to push some refs to 'u6:foo/u4/a12'" + +name "INTERNAL" diff --git a/t/t04a-wild-students b/t/t04a-wild-students new file mode 100644 index 0000000..a37a6ab --- /dev/null +++ b/t/t04a-wild-students @@ -0,0 +1,142 @@ +# vim: syn=sh: +# ---------- + +name "INTERNAL" +echo " + @prof = u1 + @TAs = u2 u3 + @students = u4 u5 u6 + + repo foo/CREATOR/a[0-9][0-9] + C = @students + RW+ = CREATOR + RW = WRITERS @TAs + R = READERS @prof + +" | ugc +notexpect ABORT + +# reasonably complex setup; we'll do everything from one repo though +cd ~/td + +name "u1 create fail" +runlocal git clone u1:foo/u1/a01 +expect "R access for foo/u1/a01 DENIED to u1" + +name "u2 create fail" +runlocal git clone u2:foo/u2/a02 +expect "R access for foo/u2/a02 DENIED to u2" + +name "u4 tries to create u2 repo" +runlocal git clone u4:foo/u2/a12 +expect "R access for foo/u2/a12 DENIED to u4" + +name "line anchored regexes" +runlocal git clone u4:foo/u4/a1234 +expect "R access for foo/u4/a1234 DENIED to u4" + +name "u4 tries to create his own repo" +runlocal git clone u4:foo/u4/a12 +expect "Initialized empty Git repository in /home/gitolite-test/repositories/foo/u4/a12.git/" +expect "warning: You appear to have cloned an empty repository." + +name "u4 push success" +cd ~/td/a12 +mdc;mdc;mdc;mdc +runlocal git push origin master +expect "To u4:foo/u4/a12" +expect "\* \[new branch\] master -> master" + +name "u1 clone success" +cd ~/td +runlocal git clone u1:foo/u4/a12 u1a12 +expect "Initialized empty Git repository in /home/tester/td/u1a12/.git/" + +name "u1 push fail" +cd ~/td/u1a12 +mdc; mdc +runlocal git push +expect "W access for foo/u4/a12 DENIED to u1" + +name "u2 clone success" +cd ~/td +runlocal git clone u2:foo/u4/a12 u2a12 +expect "Initialized empty Git repository in /home/tester/td/u2a12/.git/" + +name "u2 push success" +cd ~/td/u2a12 +mdc; mdc +runlocal git push +expect "To u2:foo/u4/a12" +expect "master -> master" + +name "u2 rewind fail" +runlocal git push -f origin master^:master +expect "remote: + refs/heads/master foo/u4/a12 u2 DENIED by fallthru" +expect "remote: error: hook declined to update refs/heads/master" +expect "To u2:foo/u4/a12" +expect "\[remote rejected\] master^ -> master (hook declined)" +expect "error: failed to push some refs to 'u2:foo/u4/a12'" + +name INTERNAL +# u4 pull to sync up +cd ~/td/a12 +runlocal git pull +expect "Fast-forward" +expect "From u4:foo/u4/a12" +expect "master -> origin/master" + +name "u4 rewind success" +runlocal git reset --hard HEAD^ +runlocal git push -f +expect "To u4:foo/u4/a12" +expect "+ .* master -> master (forced update)" + +name "u5 clone fail" +cd ~/td +runlocal git clone u5:foo/u4/a12 u5a12 +expect "R access for foo/u4/a12 DENIED to u5" + +name "setperm" +echo " +R u5 +RW u6 +" | runlocal ssh u4 setperms foo/u4/a12 +expect_filesame $TESTDIR/out/t04-wild1.1 + +name "getperms" +runlocal ssh u4 getperms foo/u4/a12 +expect_filesame $TESTDIR/out/t04-wild1.2 + +name "u5 clone success" +cd ~/td +runlocal git clone u5:foo/u4/a12 u5a12 +expect "Initialized empty Git repository in /home/tester/td/u5a12/.git/" + +name "u5 push fail" +cd ~/td/u5a12 +mdc; mdc +runlocal git push +expect "W access for foo/u4/a12 DENIED to u5" + +name "u6 clone success" +cd ~/td +runlocal git clone u6:foo/u4/a12 u6a12 +expect "Initialized empty Git repository in /home/tester/td/u6a12/.git/" + +name "u6 push success" +cd ~/td/u6a12 +mdc; mdc +runlocal git push u6:foo/u4/a12 +expect "To u6:foo/u4/a12" +expect "master -> master" + +name "u6 rewind fail" +runlocal git push -f u6:foo/u4/a12 master^:master +expect "remote: + refs/heads/master foo/u4/a12 u6 DENIED by fallthru" +expect "remote: error: hook declined to update refs/heads/master" +expect "To u6:foo/u4/a12" +expect "\[remote rejected\] master^ -> master (hook declined)" +expect "error: failed to push some refs to 'u6:foo/u4/a12'" + +name "INTERNAL" diff --git a/t/t05-delegation b/t/t05-delegation new file mode 100644 index 0000000..ad2c36b --- /dev/null +++ b/t/t05-delegation @@ -0,0 +1,11 @@ +# vim: syn=sh: +# ---------- + +for i in 0 1 +do + hl t05-delegation with GL_BIG_CONFIG $i + + . ./t05a-delegation $i + + cd $TESTDIR +done diff --git a/t/t05a-delegation b/t/t05a-delegation new file mode 100644 index 0000000..40243d0 --- /dev/null +++ b/t/t05a-delegation @@ -0,0 +1,95 @@ +# vim: syn=sh: +cd $TESTDIR +$TESTDIR/rollback +editrc GL_BIG_CONFIG $1 +# ---------- + +name "INTERNAL" +echo " + # group your projects/repos however you want + @u1r = r1a r1b + @u2r = r2a r2b + @u3r = r3a r3b + + # the admin repo access was probably like this to start with: + repo gitolite-admin + RW = u1 u2 u3 + RW+ NAME/ = tester + RW NAME/conf/fragments/u1r = u1 + RW NAME/conf/fragments/u2r = u2 + RW NAME/conf/fragments/u3r = u3 +" | ugc +notexpect ABORT + +name "tester push frag u1r" +cd ~/gitolite-admin +mkdir -p conf/fragments +echo " + repo @u1r + RW+ = tester +" > conf/fragments/u1r.conf +ugc < /dev/null +expect "create mode 100644 conf/fragments/u1r.conf" +expect "remote: Initialized empty Git repository in /home/gitolite-test/repositories/r1a.git/" + +expect "To gitolite:gitolite-admin" +expect "master -> master" + +name "u1 push frag u1r" +cd ~/gitolite-admin +echo " + repo @u1r + RW+ = u5 +" > conf/fragments/u1r.conf +ugc u1 < /dev/null +expect "To u1:gitolite-admin" +expect "master -> master" + +name "u2 push main conf fail" +cd ~/gitolite-admin +echo " + repo @u1r + RW+ = u6 +" | ugc u2 +expect "W NAME/conf/gitolite.conf gitolite-admin u2 DENIED by fallthru" +expect "To u2:gitolite-admin" +expect "\[remote rejected\] master -> master (hook declined)" +git reset --hard origin/master &>/dev/null + +name "u2 push frag u1r fail" +cd ~/gitolite-admin +echo " + repo @u1r + RW+ = u6 +" > conf/fragments/u1r.conf +ugc u2 < /dev/null +expect "remote: W NAME/conf/fragments/u1r.conf gitolite-admin u2 DENIED by fallthru" +expect "To u2:gitolite-admin" +expect "\[remote rejected\] master -> master (hook declined)" +git reset --hard origin/master &>/dev/null + +name "u3 set perms for r2a fail" +cd ~/gitolite-admin +echo " + repo r2a + RW+ = u6 +" > conf/fragments/u3r.conf +ugc u3 < /dev/null +expect "u3r.conf attempting to set access for r2a" +git reset --hard origin/master &>/dev/null + +name "u3 add r2b to u3r fail" +cd ~/gitolite-admin +echo " + @u3r = r2b + repo @u3r + RW+ = u6 +" > conf/fragments/u3r.conf +ugc u3 < /dev/null +[[ $1 == 0 ]] && expect "u3r.conf attempting to set access for r2b" +[[ $1 == 1 ]] && expect "defining groups is not allowed inside fragments" +[[ $1 == 1 ]] && notexpect "u3r.conf attempting to set access for r2b" +[[ $1 == 0 ]] && notexpect "defining groups is not allowed inside fragments" +git reset --hard origin/master &>/dev/null + +name INTERNAL diff --git a/t/t09-oldtests b/t/t09-oldtests new file mode 100644 index 0000000..388950b --- /dev/null +++ b/t/t09-oldtests @@ -0,0 +1,11 @@ +# vim: syn=sh: +# ---------- + +for i in 0 1 +do + hl t09-oldtests with GL_BIG_CONFIG $i + + . ./t09a-oldtests $i + + cd $TESTDIR +done diff --git a/t/t09a-oldtests b/t/t09a-oldtests new file mode 100644 index 0000000..1ab3b6e --- /dev/null +++ b/t/t09a-oldtests @@ -0,0 +1,50 @@ +# vim: syn=sh: +cd $TESTDIR +$TESTDIR/rollback +editrc GL_BIG_CONFIG $1 + +name "bad repo name" +echo " + repo abc*def + RW = tester +" | ugc +expect "To gitolite:gitolite-admin" +expect "master -> master" +[[ $1 == 0 ]] && expect "ABORTING" +[[ $1 == 0 ]] && expect "bad reponame abc\*def or you forgot to set .GL_WILDREPOS" + +name "bad user name" +echo " + repo abc + RW = sitaram*tester + + repo abcdef + RW = sitaram +" | ugc -r +expect "ABORTING" +expect "bad username sitaram\*tester" + +name "NAME deny" +echo " + repo abc + RW = u1 + - NAME/i = u1 + RW NAME/j = u1 + RW NAME/u = u1 +" | ugc -r +notexpect "failed to push" + +cd ~/td +runlocal git clone u1:abc +expect "Initialized empty Git repository in /home/tester/td/abc/.git/" +cd ~/td/abc +mdc jfile; runlocal git push origin master +expect "To u1:abc" +expect "\[new branch\] master -> master" +mdc ufile; runlocal git push origin master +expect "To u1:abc" +expect "master -> master" +mdc ifile; runlocal git push origin master +expect "remote: W NAME/ifile u1 DENIED by NAME/i" + +name INTERNAL diff --git a/t/t50-sequence-test b/t/t50-sequence-test new file mode 100644 index 0000000..6014e50 --- /dev/null +++ b/t/t50-sequence-test @@ -0,0 +1,93 @@ +# vim: syn=sh: +for bc in 0 1 +do + cd $TESTDIR + $TESTDIR/rollback + editrc GL_WILDREPOS 1 + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "INTERNAL" + echo " + @staff = u1 u2 u3 + repo foo/CREATOR/.+ + C = u1 + RW+ = CREATOR + RW = WRITERS + - = @staff + " | ugc + + cd ~/td + runlocal git clone u1:foo/u1/bar + expect "Initialized empty Git repository in /home/gitolite-test/repositories/foo/u1/bar.git/" + + cd bar + mdc u1file1 + runlocal git push origin master + expect "To u1:foo/u1/bar" + expect "\[new branch\] master -> master" + echo RW u2 | runlocal ssh u1 setperms foo/u1/bar + runlocal ssh u1 getperms foo/u1/bar + expect "RW u2" + name "expand" + runlocal ssh u2 expand + expect "R W.(u1).foo/u1/bar" + + name "push" + cd ~/td + runlocal git clone u2:foo/u1/bar u2bar + expect "Initialized empty Git repository in /home/tester/td/u2bar/.git/" + cd u2bar + mdc u2file1 + runlocal git push + expect "master -> master" + notexpect "DENIED" + notexpect "failed to push" + + name "INTERNAL" + + cd $TESTDIR + $TESTDIR/rollback + editrc GL_WILDREPOS 1 + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "INTERNAL" + echo " + @staff = u1 u2 u3 + repo foo/CREATOR/.+ + C = u1 + RW+ = CREATOR + - = @staff + RW = WRITERS + " | ugc -r + + cd ~/td + runlocal git clone u1:foo/u1/bar + expect "Initialized empty Git repository in /home/gitolite-test/repositories/foo/u1/bar.git/" + + cd bar + mdc u1file1 + runlocal git push origin master + expect "To u1:foo/u1/bar" + expect "\[new branch\] master -> master" + echo RW u2 | runlocal ssh u1 setperms foo/u1/bar + runlocal ssh u1 getperms foo/u1/bar + expect "RW u2" + name "expand" + runlocal ssh u2 expand + expect "R W.(u1).foo/u1/bar" + + name "push" + cd ~/td + runlocal git clone u2:foo/u1/bar u2bar + expect "Initialized empty Git repository in /home/tester/td/u2bar/.git/" + cd u2bar + mdc u2file1 + runlocal git push + expect "remote: W refs/heads/master u2 DENIED by refs/.\*" + + name INTERNAL +done diff --git a/t/t51-personal-branches b/t/t51-personal-branches new file mode 100644 index 0000000..dbbeb55 --- /dev/null +++ b/t/t51-personal-branches @@ -0,0 +1,88 @@ +# vim: syn=sh: +for wr in 0 1 +do + for bc in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback + editrc GL_WILDREPOS $wr + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "INTERNAL" + echo " + @staff = u1 u2 u3 u4 u5 u6 + repo foo + RW+ = u1 u2 + RW+ personal/USER/ = u3 u4 + RW temp = u5 u6 + " | ugc + + cd ~/td + runlocal git clone u1:foo + + cd foo + mdc; mdc; mdc; mdc; mdc + + name "u1 and u2 can push" + runlocal git push origin master + expect_push_ok "master -> master" + runlocal git push u2:foo master:personal/u1/foo + expect_push_ok "master -> personal/u1/foo" + runlocal git push origin master:personal/u2/foo + expect_push_ok "master -> personal/u2/foo" + runlocal git push u1:foo master:personal/u3/foo + expect_push_ok "master -> personal/u3/foo" + + mdc; + name "u3 cant push u1/u4 personal branches" + runlocal git push u3:foo master:personal/u1/foo + expect "remote: W refs/heads/personal/u1/foo foo u3 DENIED by fallthru" + expect "\[remote rejected\] master -> personal/u1/foo (hook declined)" + expect "failed to push" + runlocal git push u3:foo master:personal/u4/foo + expect "remote: W refs/heads/personal/u4/foo foo u3 DENIED by fallthru" + expect "\[remote rejected\] master -> personal/u4/foo (hook declined)" + expect "failed to push" + name "u4 can push u4 personal branch" + runlocal git push u4:foo master:personal/u4/foo + expect_push_ok "master -> personal/u4/foo" + name "u5 push temp" + runlocal git push u5:foo master:temp + expect_push_ok "master -> temp" + + runlocal git reset --hard HEAD^^^ + + name "u1 and u2 can rewind" + runlocal git push -f origin master + expect "master -> master (forced update)" + runlocal git push -f u2:foo master:personal/u1/foo + expect "master -> personal/u1/foo (forced update)" + runlocal git push -f origin master:personal/u2/foo + expect "master -> personal/u2/foo (forced update)" + runlocal git push -f u1:foo master:personal/u3/foo + expect "master -> personal/u3/foo (forced update)" + + name "u3 cant rewind u1/u4 personal branches" + runlocal git reset --hard HEAD^; mdc + runlocal git push -f u3:foo master:personal/u1/foo + expect "remote: + refs/heads/personal/u1/foo foo u3 DENIED by fallthru" + expect "\[remote rejected\] master -> personal/u1/foo (hook declined)" + expect "failed to push" + runlocal git push -f u3:foo master:personal/u4/foo + expect "remote: + refs/heads/personal/u4/foo foo u3 DENIED by fallthru" + expect "\[remote rejected\] master -> personal/u4/foo (hook declined)" + expect "failed to push" + name "u4 can rewind u4 personal branch" + runlocal git push -f u4:foo master:personal/u4/foo + expect "master -> personal/u4/foo (forced update)" + name "u5 cant rewind temp" + runlocal git push -f u5:foo master:temp + expect "remote: + refs/heads/temp foo u5 DENIED by fallthru" + expect "\[remote rejected\] master -> temp (hook declined)" + expect "failed to push" + + name INTERNAL + done +done diff --git a/t/t52-deny-create-ref b/t/t52-deny-create-ref new file mode 100644 index 0000000..ff04f8c --- /dev/null +++ b/t/t52-deny-create-ref @@ -0,0 +1,51 @@ +# vim: syn=sh: +for wr in 0 1 +do + for bc in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback + editrc GL_WILDREPOS $wr + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + repo foo + RW+ = @leads + - CREATE_REF = @devs + RW+ = @devs + " | ugc + + cd ~/td + runlocal git clone u1:foo + + cd foo + mdc; mdc; mdc; mdc; mdc + + name "u1 can push master" + runlocal git push origin master + expect_push_ok "master -> master" + + name "u2 can create newbr1" + runlocal git push u2:foo master:newbr1 + expect_push_ok "master -> newbr1" + + name "u3 can push newbr1" + mdc; mdc; mdc; mdc; mdc + runlocal git push u3:foo master:newbr1 + expect_push_ok "master -> newbr1" + + name "u4 canNOT push newbr3" + mdc; mdc; mdc; mdc; mdc + runlocal git push u3:foo master:newbr3 + expect "remote: W refs/heads/CREATE_REF u3 DENIED by refs/heads/CREATE_REF" + expect "\[remote rejected\] master -> newbr3 (hook declined)" + expect "failed to push" + + name INTERNAL + done +done diff --git a/t/test-driver.sh b/t/test-driver.sh new file mode 100755 index 0000000..fbfbce0 --- /dev/null +++ b/t/test-driver.sh @@ -0,0 +1,160 @@ +#!/bin/bash + +# see some sample tests for how to use these functions; there is not +# documentation + +testnum=0 +subtests=0 + +# remote local command +runlocal() { "$@" > ~/1 2> ~/2; } +# remote run command +runremote() { ssh gitolite-test@localhost "$@" > ~/1 2> ~/2; } +# remote list repositories +listrepos() { ssh gitolite-test@localhost find repositories -type d -name "*.git" | sort > ~/1 2> ~/2; } +# remote cat compiled pm +catconf() { ssh gitolite-test@localhost cat .gitolite/conf/gitolite.conf-compiled.pm > ~/1 2> ~/2; } +# remote cat ~/.gitolite.rc +catrc() { ssh gitolite-test@localhost cat .gitolite.rc > ~/1 2> ~/2; } +# tail gitolite logfile +taillog() { ssh gitolite-test@localhost tail $1 .gitolite/logs/gitolite-????-??.log > ~/1 2> ~/2; } +hl() { # highlight function + normal=`tput sgr0` + red=`tput sgr0; tput setaf 1; tput bold` + if [[ -n $1 ]] + then + echo $red"$@"$normal + else + echo $red + cat + echo $normal + fi +} +pause() { echo pausing\; hit enter or ctrl-c...; read; } + +capture() { cf=$1; shift; "$@" >& $TESTDIR/$cf; } + +editrc() { + scp gitolite-test@localhost:.gitolite.rc ~/junk >/dev/null + perl -pi -e "print STDERR if /$1/ and s/=.*/= $2;/" ~/junk + scp ~/junk gitolite-test@localhost:.gitolite.rc >/dev/null +} + +ugc () +{ + ( + cd ~/gitolite-admin; + [[ $1 == -r ]] && { + shift + cat $TESTDIR/basic.conf > conf/gitolite.conf + } + cat >> conf/gitolite.conf + git add conf keydir; + git commit --allow-empty -m "$TESTNAME"; + git push ${1:-gitolite}:gitolite-admin master + git fetch origin >/dev/null 2>&1 + ) >~/1 2>~/2 +} + +mdc() +{ + ( + echo $RANDOM > ${1:-$RANDOM} + git add . + git commit -m "$TESTNAME" + ) >~/1 2>~/2 +} + +# flush result of last test when next one comes along +testdone() { + [[ $subtests > 1 ]] && TESTNAME="($subtests) $TESTNAME" + echo -e $testnum\\t$TESTNAME +} + +# set test name/desc +name() { + if [[ -n $TESTNAME ]] + then + if [[ $TESTNAME != INTERNAL ]] + then + (( testnum++ )) + testdone + fi + subtests=0 + fi + export TESTNAME="$*" +} + +notok() { + echo ---------- + head -999 ~/1 ~/2 | sed -e 's/^/ /' +} + +expect_filesame() { + if cmp ~/1 "$1" + then + (( subtests++ )) + else + echo files ~/1 and "$1" are different + echo '*** ABORTING ***' + exit 1 + fi +} + +die() { + echo '***** AAAAARRRGGH! *****' + echo ${BASH_LINENO[1]} ${BASH_SOURCE[2]} + read + cd $TESTDIR + vim +${BASH_LINENO[1]} '+r !head ~/1 ~/2 /dev/null' ${BASH_SOURCE[2]} + exit 1 +} + +expect() { + if cat ~/1 ~/2 | grep "$1" >/dev/null + then + (( subtests++ )) + else + notok + echo ---------- + echo " expecting: $1" + echo ---------- + die $TESTNAME + exit 1 + fi +} + +notexpect() { + if cat ~/1 ~/2 | grep "$1" >/dev/null + then + notok + echo "NOT expecting: $1" + echo ---------- + die $TESTNAME + exit 1 + else + (( subtests++ )) + fi +} + +print_summary() { + echo -e "==========\n$testnum tests succeeded" +} + +expect_push_ok() { + expect "$1" + notexpect "DENIED" + notexpect "failed to push" +} + +export TESTDIR=$PWD +arg1=$1; shift +for testfile in ${arg1:-t??-}* +do + hl $testfile + . $testfile "$@" + cd $TESTDIR + hl $testfile DONE +done + +print_summary diff --git a/t/update-gitolite b/t/update-gitolite new file mode 100755 index 0000000..55af1ef --- /dev/null +++ b/t/update-gitolite @@ -0,0 +1,14 @@ +#!/bin/bash + +export TESTDIR=$PWD + +rm -rf ~/junk.ga +mv ~/gitolite-admin ~/junk.ga + +# install it +src/gl-easy-install -q gitolite-test localhost tester + +# make the rollback.tar files on both sides +cd +tar cf rollback.tar gitolite-admin +ssh gitolite-test@localhost tar cf rollback.tar .ssh .gitolite .gitolite.rc repositories gitolite-install From 255a4ca9fa202fc73d20e1b6dd4f14f652272a55 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 12 Jun 2010 11:43:53 +0530 Subject: [PATCH 389/850] add a new test to make sure "info" and "expand" show the right output --- t/t53-check-info-expand-output | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 t/t53-check-info-expand-output diff --git a/t/t53-check-info-expand-output b/t/t53-check-info-expand-output new file mode 100644 index 0000000..b5bbbbc --- /dev/null +++ b/t/t53-check-info-expand-output @@ -0,0 +1,50 @@ +# vim: syn=sh: +for wr in 0 1 +do + for bc in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback + editrc GL_WILDREPOS $wr + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + repo foo/CREATOR/.+ + C = @leads + RW+ = CREATOR + " | ugc + + cd ~/td + + name "u1 makes a wildrepo" + runlocal git clone u1:foo/u1/bar + [ "$wr" = "0" ] && expect "R access for foo/u1/bar DENIED to u1" + [ "$wr" = "0" ] && expect "fatal: The remote end hung up unexpectedly" + [ "$wr" = "1" ] && expect "Initialized empty Git repository in /home/gitolite-test/repositories/foo/u1/bar.git/" + + name "u3 fails to make a wildrepo" + runlocal git clone u3:foo/u3/baz + expect "R access for foo/u3/baz DENIED to u3" + expect "fatal: The remote end hung up unexpectedly" + + name "u1 info" + runlocal ssh u1 info + expect "hello u1, the gitolite version here is v" + expect "@R.*@W.*testing" + [ "$wr" = "1" ] && expect "C.*R.*W.*foo/u1/\\.\\+" + + name "u1 expand" + runlocal ssh u1 expand + [ "$wr" = "0" ] && expect "wildrepos disabled, sorry" + [ "$wr" = "1" ] && expect "hello u1, the gitolite version here is v" + [ "$wr" = "1" ] && expect "R.*W.*(u1).*foo/u1/bar" + [ "$wr" = "1" ] && expect "@R.*@W.*testing" + + name INTERNAL + done +done From 517786572d89e8e1e8bdf4ed13d834c47700ef30 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 12 Jun 2010 16:02:07 +0530 Subject: [PATCH 390/850] (adc fork) fork is now fast and space-efficient on the server uses "git clone -l" then manually sets up the gl-creater and hooks (thanks to Jeff and the kde gang for asking ;-) --- contrib/adc/fork | 14 ++++++-------- doc/admin-defined-commands.mkd | 9 --------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/contrib/adc/fork b/contrib/adc/fork index d5b065a..b61f415 100755 --- a/contrib/adc/fork +++ b/contrib/adc/fork @@ -5,18 +5,16 @@ to=$2 . $(dirname $0)/adc.common-functions -# we cannot just say "cd $GL_REPO_BASE_ABS; git clone $from.git $to.git". That -# won't set up the hooks or the gl-creater file that gitolite needs. So we now -# have a new "git-init" command! - get_rights_and_owner $from [ -z "$perm_read" ] && die "no read permissions on $from" get_rights_and_owner $to [ -z "$perm_create" ] && die "no create permissions on $to" -# let gitolite create the repo first -SSH_ORIGINAL_COMMAND="git-init '$to'" $GL_BINDIR/gl-auth-command $GL_USER -# then copy the refs from $from +# clone $from to $to +git clone --bare -l $GL_REPO_BASE_ABS/$from.git $GL_REPO_BASE_ABS/$to.git + +# fix up creator, and hooks cd $GL_REPO_BASE_ABS/$to.git -git fetch $GL_REPO_BASE_ABS/$from.git refs/*:refs/* +echo $GL_USER > gl-creater +cp -R $GL_REPO_BASE_ABS/$from.git/hooks/* $GL_REPO_BASE_ABS/$to.git/hooks diff --git a/doc/admin-defined-commands.mkd b/doc/admin-defined-commands.mkd index 7db971d..61e4b6a 100644 --- a/doc/admin-defined-commands.mkd +++ b/doc/admin-defined-commands.mkd @@ -167,15 +167,6 @@ the client side: or some such incantation. -**Implementation note**: you might notice that this script does not just do a -"git clone ...". This is because creating a repo has to be done via gitolite. -Otherwise, hooks won't get setup, and the all-important "gl-creater" file -showing who owns the repo won't get created. - -So now we have an actual command to just create a repo and do nothing else: -`ssh git@server git-init \'reponame\'`. [Yes; those single quotes are -required. Deal with it.] - #### rmrepo From 97b094bccb2df43d6d24c2e224038efba8a395da Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 15 Jun 2010 23:01:22 +0530 Subject: [PATCH 391/850] some doc updates (plus CHANGELOG) --- conf/example.conf | 5 +++++ doc/2-admin.mkd | 21 ++++++++++++++++++++- doc/3-faq-tips-etc.mkd | 25 +++++++++++++++++++++++++ doc/CHANGELOG | 22 +++++++++++++++++++--- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 5a5b36a..cde1130 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -42,6 +42,11 @@ # ...or a group of repos @oss_repos = gitolite linux git perl rakudo entrans vkc + # ...or even a group of refexes +@important = master$ QA_done refs/tags/v[0-9] + # (see later for what "refex"s are; I'm only mentioning it + # here to emphasise that you can group them too) + # even sliced and diced differently @admins = sitaram admin2 # notice that sitaram is in 2 groups (staff and admins) diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 35bff40..6c0b036 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -12,6 +12,7 @@ In this document: * specifying gitweb and daemon access * custom hooks * hook chaining + * environment variables available to hooks * custom git config ---- @@ -42,7 +43,9 @@ Once you've cloned it, you're ready to add users and repos. extension, like `sitaram.pub` or `john-smith.pub`. You can also use periods and underscores - * copy all these `*.pub` files to `keydir` in your gitolite-admin repo clone + * copy all these `*.pub` files to `keydir` in your gitolite-admin repo + clone. You can also organise them into various subdirectories of `keydir` + if you wish, since the entire tree is searched. * edit the config file (`conf/gitolite.conf` in your admin repo clone). See `conf/example.conf` in the gitolite source for details on what goes in @@ -159,6 +162,22 @@ Finally, these names (`update.secondary` and `post-update.secondary`) are merely the defaults. You can change them to anything you want; look in conf/example.gitolite.rc for details. + + +#### environment variables available to hooks + +The following environment variables are set, and may be useful for any custom +processing you wish to do in your hook code: + + * `GL_USER` -- the user doing the push + * `GL_REPO` -- the reponame + * `GL_REPO_BASE_ABS` -- the absolute base path where all the repos are kept + +The following variables are also set, but are generally less useful: + + * `GL_BINDIR` -- where all the binaries live + * `GL_ADMINDIR` -- common directory for many gitolite things + #### custom git config diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index fc8663f..3ad27fd 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -27,6 +27,8 @@ In this document: * support for git installed outside default PATH * "personal" branches * custom hooks and custom git config + * INconvenience features + * deleting a repo * helping with gitweb * easier to specify gitweb "description" and gitweb/daemon access * easier to link gitweb authorisation with gitolite @@ -545,6 +547,29 @@ You can specify hooks that you want to propagate to all repos, as well as per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and `conf/example.conf` for details. + + +#### INconvenience features + + + +##### deleting a repo + +By design, there is no code in gitolite to *delete* a repo if the repo was +specified by name in the config file. (Wildcard repos *can* be deleted by the +user; see [here][rmrepo] for details). + +[rmrepo]: http://github.com/sitaramc/gitolite/blob/pu/doc/admin-defined-commands.mkd#rmrepo + +If you *do* want to permanently delete a *non*-wildcard repo, here's what you +do: + + * remove the repo from the gitolite-admin repo clone's `conf/gitolite.conf` + file. "add" the change, commit, and push. + + * *then* remove the repo from `~/repositories` on the server (or whatever + you set `$GL_REPO_BASE` to in the `~/.gitolite.rc`) + #### helping with gitweb diff --git a/doc/CHANGELOG b/doc/CHANGELOG index fdfd39f..60b1f61 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,8 +2,24 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] - - v1.5.1 -- tries to eliminate the need to run gl-setup on data version - change, thus hopefully obsoleting the upgrade note for v1.5 (just below). + - some small but important doc updates + - adc "fork" now much faster (uses git clone -l) + + - v1.5.2 + + - added test suite + + - v1.5.1 + + - disallow creation of new refs if you want (while allowing push of the + same) + + - adc "able" command added to contrib + - easy-install now takes a host-nickname parameter for convenience in + installing more than one gitolite server + - major doc revamp; contrib/autotoc added to make docs look nicer + - eliminate the need to run gl-setup on data version change, thus hopefully + obsoleting the upgrade note for v1.5 (just below). - v1.5 -- IMPORTANT UPGRADE NOTES below @@ -61,7 +77,7 @@ the tags can help you position stuff approximately - contrib directory added - expand now lists non-wildcard repos also - - refs also have groups now (NYD) + - refs also have groups now - allow admins to get "info" for other users - wildrepos merged From 1ecc7ae74ed23a64fe18d76e2846fa2b2a619a37 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 16 Jun 2010 14:19:31 +0530 Subject: [PATCH 392/850] (minor) added overkill doc --- doc/overkill.mkd | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 doc/overkill.mkd diff --git a/doc/overkill.mkd b/doc/overkill.mkd new file mode 100644 index 0000000..bb85e89 --- /dev/null +++ b/doc/overkill.mkd @@ -0,0 +1,51 @@ +# when gitolite is overkill + +Note: I wrote this to help people for whom gitolite is genuinely overkill. I +believe it will all work, but YMMV. + +---- + +You don't always need something like gitolite. If you have a fixed (or very +rarely changing) number of users, and all of them have full access to all your +repos, you can use plain Unix permissions to get a lot of this done: + + * dedicate a userid (say "git") to host all your repos. This user will also + have a group (normally called "git" on most distros I think) + + * create a directory that is accessible (at least "r" and "x" permissions) + to the group "git", all the way upto the root. (That is, if the directory + you chose is /home/git/repos, then /, /home, /home/git, and + /home/git/repos must all be "g+rx"). + + * create all repos in this directory, as the "git" user, using the following + command: + + git init --bare --shared reponame.git + + * For each user who needs access to the repos, add them as members to the + "git" group also. On Mandriva this is: + + usermod -G git username + + Don't forget that `-G` *replaces* the list of supplementary groups for the + user, so be sure to first check if he is already member of some groups and + keep those in the command (comma-separated). + +And that's basically it. The "init --shared" will create the repos with +"chmod -R g+s". If you have existing repos where you forgot (or didn't know) +the "--shared" argument, do this on each of them: + + cd reponame.git + git init --shared --bare + chmod -R g+w . + chmod g+s `find . -type d` + +I think that should do it. + +---- + +You can do more complex things using Unix acls. If you do, and feel like +writing it up, send it to me and I will add it here (with credit given of +course). Personally, I can't be bothered -- once you have differing needs for +different people, you really need gitolite anyway, because you probably need +different rights for branches as well and Unix ACLs can't do that. From 0f5f82e4f55d9c0dcc8b07f77aec0655e3168d4e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 16 Jun 2010 07:20:12 +0530 Subject: [PATCH 393/850] log message changes (warning: minor backward compat breakage) The log message format has changed. All log messages now have a common prefix (timestamp, user, IP). This is followed by $SSH_ORIGINAL_COMMAND (or, in one special case, the name of the user's login shell). Any further text appears after this (currently this only happens in the case of a successful push -- one for each ref pushed successfully) --- doc/CHANGELOG | 6 +++++- hooks/common/update | 5 ++--- src/gitolite.pm | 14 +++++++++++--- src/gl-auth-command | 6 +++--- t/t00-initial | 2 +- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 60b1f61..978e03f 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,8 +2,12 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] + - v1.5.3 + + - log file format changed; minor backward compat breakage if you've been + doing any automated log processing - some small but important doc updates - - adc "fork" now much faster (uses git clone -l) + - adc "fork" now much faster and more space-efficient (uses git clone -l) - v1.5.2 diff --git a/hooks/common/update b/hooks/common/update index 219157d..51def0d 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -105,9 +105,8 @@ my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, (shift @refs), $att_acc # if we returned at all, all the checks succeeded, so we log the action and exit 0 -&log_it("$ENV{GL_TS} $att_acc\t" . - substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . - "\t$reported_repo\t$ref\t$ENV{GL_USER}\t$log_refex\n"); +&log_it("", "$att_acc\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) . + "\t$reported_repo\t$ref\t$log_refex"); # now chain to the local admin defined update hook, if present $UPDATE_CHAINS_TO ||= 'hooks/update.secondary'; diff --git a/src/gitolite.pm b/src/gitolite.pm index 776c800..875d83e 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -58,8 +58,16 @@ sub wrap_open { } sub log_it { + my ($ip, $logmsg); open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n"; - print $log_fh @_; + # first space sep field is client ip, per "man ssh" + ($ip = $ENV{SSH_CONNECTION}) =~ s/ .*//; + # the first part of logmsg is the actual command used; it's either passed + # in via arg1, or picked up from SSH_ORIGINAL_COMMAND + $logmsg = $_[0] || $ENV{SSH_ORIGINAL_COMMAND}; shift; + # the rest of it upto the caller; we just dump it into the logfile + $logmsg .= "\t@_" if @_; + print $log_fh "$ENV{GL_TS}\t$ENV{GL_USER}\t$ip\t$logmsg\n"; close $log_fh or die "close log failed: $!\n"; } @@ -474,7 +482,7 @@ sub special_cmd &ext_cmd_svnserve($SVNSERVE); } else { # if the user is allowed a shell, just run the command - &log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{GL_USER}\n"); + &log_it(); exec $ENV{SHELL}, "-c", $cmd if $shell_allowed; die "bad command: $cmd\n"; @@ -615,7 +623,7 @@ sub ext_cmd_rsync # that should "die" if there's a problem wrap_chdir($RSYNC_BASE); - &log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{GL_USER}\n"); + &log_it(); exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND}; } diff --git a/src/gl-auth-command b/src/gl-auth-command index bcd43b4..8ca43d9 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -102,7 +102,7 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { if ($shell_allowed) { my $shell = $ENV{SHELL}; $shell =~ s/.*\//-/; # change "/bin/bash" to "-bash" - &log_it("$ENV{GL_TS}\t$shell\t$user\n"); + &log_it($shell); exec { $ENV{SHELL} } $shell; } # otherwise, pretend he typed in "info" and carry on... @@ -119,7 +119,7 @@ if ($GL_ADC_PATH and -d $GL_ADC_PATH) { if (-x "$GL_ADC_PATH/$cmd") { # yes this is rather strict, sorry. do { die "I don't like $_\n" unless $_ =~ $REPOPATT_PATT } for ($cmd, @args); - &log_it("$ENV{GL_TS}\t$GL_ADC_PATH/$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{GL_USER}\n"); + &log_it("$GL_ADC_PATH/$ENV{SSH_ORIGINAL_COMMAND}"); exec("$GL_ADC_PATH/$cmd", @args); } } @@ -208,7 +208,7 @@ die "$aa access for $repo DENIED to $user\n" unless $perm =~ /$aa/; # over to git now # ---------------------------------------------------------------------------- -&log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$ENV{GL_USER}\n"); +&log_it(); $repo = "'$REPO_BASE/$repo.git'"; exec("git", "shell", "-c", "$verb $repo") unless $verb eq 'git-init'; diff --git a/t/t00-initial b/t/t00-initial index 7ff8157..39c1cbe 100644 --- a/t/t00-initial +++ b/t/t00-initial @@ -53,7 +53,7 @@ runlocal git push -f origin HEAD expect "+ .* HEAD -> master (forced update)" name "basic rewind log" taillog -expect "\+.*aa.refs/heads/master.u1.refs/.\*" +expect " u1 .* + .* aa refs/heads/master refs/.\*" # ---------- name "basic rewind deny" From bf1a9720af9a0732d05dded8fc235afe193fbe8b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Jun 2010 16:22:17 +0530 Subject: [PATCH 394/850] (minor) be less noisy about pubkeys present but not used in config The main use case is for people who give most people access via @all, which is somewhat unusual but in some situations it probably makes sense. See also a related commit made a month or so ago (aa8da93). Actually these two lint checks were made to help people spot typos in the config, which sorta becomes meaningless if you have more than a few such cases anyway, so for most people it should not matter that I am now merely summarising the number of such cases if there are more then 10. --- src/gl-compile-conf | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 334259e..2e75e83 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -553,6 +553,7 @@ while (<$authkeys_fh>) # options, in the standard ssh authorized_keys format), then the "end" line. print $newkeys_fh "# gitolite start\n"; wrap_chdir($GL_KEYDIR); +my @not_in_config; # pubkeys exist but users don't appear in the config file for my $pubkey (`find . -type f`) { chomp($pubkey); $pubkey =~ s(^\./)(); @@ -574,9 +575,8 @@ for my $pubkey (`find . -type f`) $user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz - # lint check 2 - print STDERR "WARNING: pubkey $pubkey exists but user $user not in config\n" - unless $user_list{$user}; + # lint check 2 -- don't print right now; just collect the messages + push @not_in_config, "$user($pubkey)" unless $user_list{$user}; $user_list{$user} = 'has pubkey'; # apparently some pubkeys don't end in a newline... my $pubkey_content = `cat $pubkey`; @@ -590,6 +590,14 @@ for my $pubkey (`find . -type f`) print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; print $newkeys_fh $pubkey_content; } + +# lint check 2 -- print less noisily +if (@not_in_config > 10) { + print STDERR "$WARN You have " . scalar(@not_in_config) . " pubkeys that do not appear to be used in the config\n"; +} elsif (@not_in_config) { + print STDERR "$WARN the following users (pubkey files in parens) do not appear in the config file:\n", join(",", sort @not_in_config), "\n"; +} + # lint check 3; a little more severe than the first two I guess... { my @no_pubkey = From 78c8caa24ce27f90700700c88373d9648c6ad013 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Jun 2010 19:31:06 +0530 Subject: [PATCH 395/850] Revert "now you can disallow creation of new refs if you like" This reverts commit 6d32e4e9203f5cbfcba1a27a061a748aa1b69f90. see subsequent commits for why --- contrib/gerrit.mkd | 4 ++++ doc/3-faq-tips-etc.mkd | 35 ----------------------------------- hooks/common/update | 2 -- src/gitolite.pm | 2 +- 4 files changed, 5 insertions(+), 38 deletions(-) diff --git a/contrib/gerrit.mkd b/contrib/gerrit.mkd index e55cf47..679f1a9 100644 --- a/contrib/gerrit.mkd +++ b/contrib/gerrit.mkd @@ -92,6 +92,10 @@ review stuff :) * gitolite doesnt do anything special to signed or annotated tags + * gitolite always allows creating a branch. The only way to prevent that is + to list out allowed branches explicitly (make sure you end the refex with + a `$`!). + * Force push is the same as delete: historically (and by default, even now) gitolite does the same . However, I've only recently (and somewhat reluctantly) changed gitolite to allow treating these two separately. diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 3ad27fd..f5ac1da 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -18,7 +18,6 @@ In this document: * better logging * "exclude" (or "deny") rules * separating delete and rewind rights - * separating create and push rights * file/dir NAME based restrictions * delegating parts of the config file * convenience features @@ -384,40 +383,6 @@ message when you push, a non-existant user. [sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c# - - -##### separating create and push rights - -[note: the documentation took longer to write than the code ;-)] - -Now you can disallow creation of new refs if you like. - -Normally, when you try to update a ref (push a branch or a tag), it's checked -against all the refexes, and if none of them match for the operation you're -trying (W or +), or it matches a "-", the operation is denied. - -That is, most refs are default "deny". - -`CREATE_REF` is a "fake" refex that controls the ability to *create* a branch, -even if you are allowed to *push* changes to it. - -The right to create a ref (i.e., push a brand new one), however, defaults to -"accept" unless a deny rule is found. This is mainly for backward compat -reasons, but also because this feature is rarely needed, so there's no point -burdening everyone with having to create the opposite rule. - -So if you want to prevent someone from *creating* any branches that they -otherwise *can* push, make sure that the first rule that applies to them is a -`- CREATE_REF` rule, like line #3 below: - - repo foo - RW+ = @leads - - CREATE_REF = @devs - RW+ = @devs - -One side effect is that you now can no longer have an *actual* branch called -`CREATE_REF`. Oh well, into each life some rain must fall! - ##### file/dir NAME based restrictions diff --git a/hooks/common/update b/hooks/common/update index 51def0d..b77d6ec 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -82,8 +82,6 @@ push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; # been specified my @refs = ($ref); # the first ref to check is the real one -# if a new ref is being created, add a special ref to record that -push @refs, 'refs/heads/CREATE_REF' if $oldsha eq '0' x 40; # because making it work screws up efficiency like no tomorrow... if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) { # this is special to git -- the hash of an empty tree diff --git a/src/gitolite.pm b/src/gitolite.pm index 875d83e..c2de81f 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -95,7 +95,7 @@ sub check_ref { # as far as *this* ref is concerned we're ok return $refex if ($ar->[2] =~ /\Q$perm/); } - die "$perm $ref $repo $ENV{GL_USER} DENIED by fallthru\n" unless $ref eq 'refs/heads/CREATE_REF'; + die "$perm $ref $repo $ENV{GL_USER} DENIED by fallthru\n"; } # ln -sf :-) From a430cc57c7fc82827a43524bddb7a5611edfa3a0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Jun 2010 20:14:40 +0530 Subject: [PATCH 396/850] separating "push" from "create" This is what I *should* have done back then; thanks to Jeff Mitchell for pointing out a problem with the old method. The old one is *definitely* a kludge. --- conf/example.conf | 10 ++-- doc/3-faq-tips-etc.mkd | 22 +++++++ hooks/common/update | 2 + src/gitolite.pm | 1 + src/gl-compile-conf | 3 +- t/t52-deny-create-ref | 132 +++++++++++++++++++++++++++++------------ 6 files changed, 126 insertions(+), 44 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index cde1130..4cf280d 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -72,13 +72,15 @@ # start line: # repo [one or more repos and/or repo groups] # followed by one or more permissions lines: -# (C|R|RW|RW+|RWD|RW+D) [zero or more refexes] = [one or more users] +# (C|R|RW|RW+|RWC|RW+C|RWD|RW+D|RWCD|RW+CD) [zero or more refexes] = [one or more users] # there are 6 types of permissions: R, RW, and RW+ are simple (the "+" means # permission to "rewind" -- force push a non-fast forward to -- a branch). -# The C permission is described in doc/4-wildcard-repositories.mkd. The D -# addition to RW/RW+ is described in doc/3-faq-tips-etc.mkd, in the section on -# "separating delete and rewind rights". +# The *standalone* C permission pertains to creating a REPO and is described +# in doc/4-wildcard-repositories.mkd. The C and D *suffixes* to the RW/RW+ +# permissions pertain to creating or deleting a BRANCH, and are described in +# doc/3-faq-tips-etc.mkd, in the sections on "separating push and create +# rights" and "separating delete and rewind rights" respectively. # how permissions are matched: # - user, repo, and access (W or +) are known. For that combination, if diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index f5ac1da..935e4b3 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -18,6 +18,7 @@ In this document: * better logging * "exclude" (or "deny") rules * separating delete and rewind rights + * separating create and push rights * file/dir NAME based restrictions * delegating parts of the config file * convenience features @@ -381,8 +382,29 @@ Note 2: a quick way to make this the default for *all* your repos is: where foo can be either the administrator, or if you can ignore the warning message when you push, a non-existant user. +Note 3: you can combine this with the "create a branch" permissions described +in the next section, as the example line in conf/example.conf shows. + [sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c# + + +##### separating create and push rights + +This feature is similar in spirit to the previous one, so please read that +section for a general understanding. + +Briefly: + + * branch creation is permitted by using `RWC` or `RW+C` -- essentially the + current branch permissions with a `C` suffixed + * if a repo has a rule containing such a `C`, then the `RW` and `RW+` + permissions (for that repo) no longer permit creation of the ref matched; + they will only allow pushing to an existing ref + +Note: you can combine this with the "delete a branch" permissions described in +the previous section, as the example line in conf/example.conf shows. + ##### file/dir NAME based restrictions diff --git a/hooks/common/update b/hooks/common/update index b77d6ec..5999449 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -67,6 +67,8 @@ $att_acc = '+' if $oldsha ne $merge_base; # were any 'D' perms specified? If they were, it means we have to separate # deletes from rewinds, so if the new sha is all 0's, change the '+' to a 'D' $att_acc = 'D' if ( $repos{$ENV{GL_REPO}}{DELETE_IS_D} or $repos{'@all'}{DELETE_IS_D} ) and $newsha eq '0' x 40; +# similarly C for create a branch +$att_acc = 'C' if ( $repos{$ENV{GL_REPO}}{CREATE_IS_C} or $repos{'@all'}{CREATE_IS_C} ) and $oldsha eq '0' x 40; my @allowed_refs; # @all repos: see comments in similar code in check_access diff --git a/src/gitolite.pm b/src/gitolite.pm index c2de81f..13d1451 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -291,6 +291,7 @@ sub parse_acl for my $r ('@all', @repo_plus) { my $dr = $repo; $dr = '@all' if $r eq '@all'; $repos{$dr}{DELETE_IS_D} = 1 if $repos{$r}{DELETE_IS_D}; + $repos{$dr}{CREATE_IS_C} = 1 if $repos{$r}{CREATE_IS_C}; $repos{$dr}{NAME_LIMITS} = 1 if $repos{$r}{NAME_LIMITS}; for my $u ('@all', "$gl_user - wild", @user_plus) { diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 2e75e83..f0b27fa 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -204,7 +204,7 @@ sub parse_conf_file s/\bCREAT[EO]R\b/\$creator/g for @repos; } # actual permission line - elsif (/^(-|C|R|RW\+?D?) (.* )?= (.+)/) + elsif (/^(-|C|R|RW\+?(?:C?D?|D?C?)) (.* )?= (.+)/) { my $perms = $1; my @refs; @refs = split(' ', $2) if $2; @@ -286,6 +286,7 @@ sub parse_conf_file # that fact easy to find; this changes the meaning of RW+ # to no longer permit deletes (see update hook) $repos{$repo}{DELETE_IS_D} = 1 if $perms =~ /D/; + $repos{$repo}{CREATE_IS_C} = 1 if $perms =~ /RW.*C/; # for 2nd level check, store each "ref, perms" pair in order for my $ref (@refs) diff --git a/t/t52-deny-create-ref b/t/t52-deny-create-ref index ff04f8c..9afa369 100644 --- a/t/t52-deny-create-ref +++ b/t/t52-deny-create-ref @@ -1,51 +1,105 @@ # vim: syn=sh: -for wr in 0 1 +for bc in 0 1 do - for bc in 0 1 - do - cd $TESTDIR - $TESTDIR/rollback - editrc GL_WILDREPOS $wr - editrc GL_BIG_CONFIG $bc + cd $TESTDIR + $TESTDIR/rollback + editrc GL_BIG_CONFIG $bc - # ---------- + # ---------- - name "INTERNAL" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 - repo foo - RW+ = @leads - - CREATE_REF = @devs - RW+ = @devs - " | ugc + # test "C" permissions - cd ~/td - runlocal git clone u1:foo + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - cd foo - mdc; mdc; mdc; mdc; mdc + repo foo + RW+C = @leads + RW+C personal/USER/ = @devs + RW = @devs + " | ugc - name "u1 can push master" - runlocal git push origin master - expect_push_ok "master -> master" + cd ~/td + runlocal git clone u1:foo - name "u2 can create newbr1" - runlocal git push u2:foo master:newbr1 - expect_push_ok "master -> newbr1" + cd foo + mdc; mdc; mdc; mdc; mdc - name "u3 can push newbr1" - mdc; mdc; mdc; mdc; mdc - runlocal git push u3:foo master:newbr1 - expect_push_ok "master -> newbr1" + name "u1 can push/rewind master on foo" + runlocal git push origin master + expect_push_ok "master -> master" + runlocal git push -f origin master^^:master + expect_push_ok "master^^ -> master" - name "u4 canNOT push newbr3" - mdc; mdc; mdc; mdc; mdc - runlocal git push u3:foo master:newbr3 - expect "remote: W refs/heads/CREATE_REF u3 DENIED by refs/heads/CREATE_REF" - expect "\[remote rejected\] master -> newbr3 (hook declined)" - expect "failed to push" + name "u2 can create newbr1 on foo" + runlocal git push u2:foo master:newbr1 + expect_push_ok "master -> newbr1" - name INTERNAL - done + name "u3 can push newbr1 on foo" + mdc; mdc; mdc; mdc; mdc + runlocal git push u3:foo master:newbr1 + expect_push_ok "master -> newbr1" + + name "u4 canNOT create newbr2 on foo" + mdc; mdc; mdc; mdc; mdc + runlocal git push u3:foo master:newbr2 + expect "remote: C refs/heads/newbr2 foo u3 DENIED by fallthru" + expect "hook declined" + expect "failed to push" + + name "u4 can create/rewind personal/u4/newbr3 on foo" + mdc; mdc; mdc; mdc; mdc + runlocal git push u4:foo master:personal/u4/newbr3 + expect_push_ok "master -> personal/u4/newbr3" + runlocal git push -f origin master^^:personal/u4/newbr3 + expect_push_ok "master^^ -> personal/u4/newbr3" + + # bar, without "C" permissions, should behave like old + + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar + RW+ = @leads + RW+ personal/USER/ = @devs + RW = @devs + " | ugc + + cd ~/td + runlocal git clone u1:bar + + cd bar + mdc; mdc; mdc; mdc; mdc + + name "u1 can push/rewind master on bar" + runlocal git push origin master + expect_push_ok "master -> master" + runlocal git push -f origin master^^:master + expect_push_ok "master^^ -> master" + + name "u2 can create newbr1 on bar" + runlocal git push u2:bar master:newbr1 + expect_push_ok "master -> newbr1" + + name "u3 can push newbr1 on bar" + mdc; mdc; mdc; mdc; mdc + runlocal git push u3:bar master:newbr1 + expect_push_ok "master -> newbr1" + + name "u4 can create newbr2 on bar" + mdc; mdc; mdc; mdc; mdc + runlocal git push u3:bar master:newbr2 + expect_push_ok "master -> newbr2" + + name "u4 can create/rewind personal/u4/newbr3 on bar" + mdc; mdc; mdc; mdc; mdc + runlocal git push u4:bar master:personal/u4/newbr3 + expect_push_ok "master -> personal/u4/newbr3" + runlocal git push -f origin master^^:personal/u4/newbr3 + expect_push_ok "master^^ -> personal/u4/newbr3" + + name INTERNAL done From 3c5bd4a131492e85a269bbec3eba86a9e1387c25 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Jun 2010 21:43:53 +0530 Subject: [PATCH 397/850] test suite: catch internal errors better ...so you don't have to look at all the output for any strangeness (also make rollback a little quieter) --- t/rollback | 2 +- t/t-fedora-big-config | 2 +- t/t00-initial | 4 +++- t/t01-repo-groups | 10 ++++++---- t/t02-user-groups | 10 ++++++---- t/t03-branch-permissions | 6 ++++-- t/t04-wild | 2 +- t/t05a-delegation | 2 +- t/t09a-oldtests | 2 +- t/t50-sequence-test | 4 ++-- t/t51-personal-branches | 2 +- t/t52-deny-create-ref | 2 +- t/t53-check-info-expand-output | 2 +- t/test-driver.sh | 3 +-- 14 files changed, 30 insertions(+), 23 deletions(-) diff --git a/t/rollback b/t/rollback index 4fdb260..84f3872 100755 --- a/t/rollback +++ b/t/rollback @@ -7,5 +7,5 @@ rm -rf gitolite-admin td tar xf rollback.tar mkdir td -scp $TESTDIR/rollback.server gitolite-test@localhost:rollback +scp -q $TESTDIR/rollback.server gitolite-test@localhost:rollback || { echo scp failed; exit 1; } ssh gitolite-test@localhost ./rollback diff --git a/t/t-fedora-big-config b/t/t-fedora-big-config index 6604bd0..74c9ca2 100644 --- a/t/t-fedora-big-config +++ b/t/t-fedora-big-config @@ -1,6 +1,6 @@ # vim: syn=sh: cd $TESTDIR -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 1 editrc GL_NO_DAEMON_NO_GITWEB 1 catrc diff --git a/t/t00-initial b/t/t00-initial index 39c1cbe..d708fdd 100644 --- a/t/t00-initial +++ b/t/t00-initial @@ -1,5 +1,5 @@ # vim: syn=sh: -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" # ---------- name "basic push admin repo" @@ -68,3 +68,5 @@ runlocal git reset --hard HEAD^ mdc runlocal git push -f origin HEAD expect "remote: + refs/heads/master aa u2 DENIED by fallthru" + +name INTERNAL diff --git a/t/t01-repo-groups b/t/t01-repo-groups index d879894..d738def 100644 --- a/t/t01-repo-groups +++ b/t/t01-repo-groups @@ -1,6 +1,6 @@ # vim: syn=sh: # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 0 name "base output with no groups" @@ -14,7 +14,7 @@ catconf expect_filesame $TESTDIR/out/t01-repo-groups.1 # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 0 name "output with eqvt repo groups" @@ -29,7 +29,7 @@ catconf expect_filesame $TESTDIR/out/t01-repo-groups.1 # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 1 name "base output with no groups but GL_BIG_CONFIG on" @@ -43,7 +43,7 @@ catconf expect_filesame $TESTDIR/out/t01-repo-groups.1 # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 1 name "repo groups with GL_BIG_CONFIG on" @@ -56,3 +56,5 @@ echo " catconf expect_filesame $TESTDIR/out/t01-repo-groups.2 + +name INTERNAL diff --git a/t/t02-user-groups b/t/t02-user-groups index 29d98ce..bc17e17 100644 --- a/t/t02-user-groups +++ b/t/t02-user-groups @@ -1,6 +1,6 @@ # vim: syn=sh: # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 0 name "base output with no groups" @@ -14,7 +14,7 @@ catconf expect_filesame $TESTDIR/out/t02-user-groups.1 # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 0 name "output with eqvt user groups" @@ -32,7 +32,7 @@ catconf expect_filesame $TESTDIR/out/t02-user-groups.1 # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 1 name "base output with no groups but GL_BIG_CONFIG on" @@ -46,7 +46,7 @@ catconf expect_filesame $TESTDIR/out/t02-user-groups.1 # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 1 name "user groups with GL_BIG_CONFIG on" @@ -62,3 +62,5 @@ echo " catconf expect_filesame $TESTDIR/out/t02-user-groups.2 + +name INTERNAL diff --git a/t/t03-branch-permissions b/t/t03-branch-permissions index c1766c1..f8ef19c 100644 --- a/t/t03-branch-permissions +++ b/t/t03-branch-permissions @@ -1,6 +1,6 @@ # vim: syn=sh: # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 0 echo ===== testing with GL_BIG_CONFIG set to 0 ===== @@ -10,7 +10,7 @@ cd $TESTDIR cd $TESTDIR # ---------- -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 1 echo ===== testing with GL_BIG_CONFIG set to 1 ===== @@ -18,3 +18,5 @@ echo ===== testing with GL_BIG_CONFIG set to 1 ===== cd $TESTDIR . ./t03a-branch-permissions cd $TESTDIR + +name INTERNAL diff --git a/t/t04-wild b/t/t04-wild index e7b5464..7efbf02 100644 --- a/t/t04-wild +++ b/t/t04-wild @@ -8,7 +8,7 @@ do hl t04-wild with GL_WILDREPOS $i, C=@$j cd $TESTDIR - $TESTDIR/rollback + $TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG $i editrc GL_WILDREPOS 1 . ./t04a-wild-$j diff --git a/t/t05a-delegation b/t/t05a-delegation index 40243d0..8fcdbca 100644 --- a/t/t05a-delegation +++ b/t/t05a-delegation @@ -1,6 +1,6 @@ # vim: syn=sh: cd $TESTDIR -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG $1 # ---------- diff --git a/t/t09a-oldtests b/t/t09a-oldtests index 1ab3b6e..16edb61 100644 --- a/t/t09a-oldtests +++ b/t/t09a-oldtests @@ -1,6 +1,6 @@ # vim: syn=sh: cd $TESTDIR -$TESTDIR/rollback +$TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG $1 name "bad repo name" diff --git a/t/t50-sequence-test b/t/t50-sequence-test index 6014e50..4c540f7 100644 --- a/t/t50-sequence-test +++ b/t/t50-sequence-test @@ -2,7 +2,7 @@ for bc in 0 1 do cd $TESTDIR - $TESTDIR/rollback + $TESTDIR/rollback || die "rollback failed" editrc GL_WILDREPOS 1 editrc GL_BIG_CONFIG $bc @@ -48,7 +48,7 @@ do name "INTERNAL" cd $TESTDIR - $TESTDIR/rollback + $TESTDIR/rollback || die "rollback failed" editrc GL_WILDREPOS 1 editrc GL_BIG_CONFIG $bc diff --git a/t/t51-personal-branches b/t/t51-personal-branches index dbbeb55..d040bc4 100644 --- a/t/t51-personal-branches +++ b/t/t51-personal-branches @@ -4,7 +4,7 @@ do for bc in 0 1 do cd $TESTDIR - $TESTDIR/rollback + $TESTDIR/rollback || die "rollback failed" editrc GL_WILDREPOS $wr editrc GL_BIG_CONFIG $bc diff --git a/t/t52-deny-create-ref b/t/t52-deny-create-ref index 9afa369..3aa9e83 100644 --- a/t/t52-deny-create-ref +++ b/t/t52-deny-create-ref @@ -2,7 +2,7 @@ for bc in 0 1 do cd $TESTDIR - $TESTDIR/rollback + $TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG $bc # ---------- diff --git a/t/t53-check-info-expand-output b/t/t53-check-info-expand-output index b5bbbbc..43bb938 100644 --- a/t/t53-check-info-expand-output +++ b/t/t53-check-info-expand-output @@ -4,7 +4,7 @@ do for bc in 0 1 do cd $TESTDIR - $TESTDIR/rollback + $TESTDIR/rollback || die "rollback failed" editrc GL_WILDREPOS $wr editrc GL_BIG_CONFIG $bc diff --git a/t/test-driver.sh b/t/test-driver.sh index fbfbce0..416000d 100755 --- a/t/test-driver.sh +++ b/t/test-driver.sh @@ -152,9 +152,8 @@ arg1=$1; shift for testfile in ${arg1:-t??-}* do hl $testfile - . $testfile "$@" + . $testfile "$@" || die "$testfile failed" cd $TESTDIR - hl $testfile DONE done print_summary From 286ce76048a71e14d30465aea5ccfecbf32b6d20 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 18 Jun 2010 23:16:42 +0530 Subject: [PATCH 398/850] (minor) added tags to test 52 on denying creates --- t/t52-deny-create-ref | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/t/t52-deny-create-ref b/t/t52-deny-create-ref index 3aa9e83..8e32a36 100644 --- a/t/t52-deny-create-ref +++ b/t/t52-deny-create-ref @@ -36,6 +36,11 @@ do runlocal git push u2:foo master:newbr1 expect_push_ok "master -> newbr1" + name "u2 can create newtag on foo" + runlocal git tag newtag + runlocal git push u2:foo newtag + expect_push_ok "newtag -> newtag" + name "u3 can push newbr1 on foo" mdc; mdc; mdc; mdc; mdc runlocal git push u3:foo master:newbr1 @@ -48,6 +53,13 @@ do expect "hook declined" expect "failed to push" + name "u4 canNOT create newtag2 on foo" + runlocal git tag newtag2 + runlocal git push u3:foo newtag2 + expect "remote: C refs/tags/newtag2 foo u3 DENIED by fallthru" + expect "hook declined" + expect "failed to push" + name "u4 can create/rewind personal/u4/newbr3 on foo" mdc; mdc; mdc; mdc; mdc runlocal git push u4:foo master:personal/u4/newbr3 @@ -84,6 +96,11 @@ do runlocal git push u2:bar master:newbr1 expect_push_ok "master -> newbr1" + name "u2 can create newtag on bar" + runlocal git tag newtag + runlocal git push u2:bar newtag + expect_push_ok "newtag -> newtag" + name "u3 can push newbr1 on bar" mdc; mdc; mdc; mdc; mdc runlocal git push u3:bar master:newbr1 @@ -94,6 +111,11 @@ do runlocal git push u3:bar master:newbr2 expect_push_ok "master -> newbr2" + name "u4 can create newtag2 on bar" + runlocal git tag newtag2 + runlocal git push u3:bar newtag2 + expect_push_ok "newtag2 -> newtag2" + name "u4 can create/rewind personal/u4/newbr3 on bar" mdc; mdc; mdc; mdc; mdc runlocal git push u4:bar master:personal/u4/newbr3 From bc5fc5793dcc6af2886603c92452d2b72ad41461 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 19 Jun 2010 11:33:18 +0530 Subject: [PATCH 399/850] (minor) added hook propagation doc --- doc/hook-propagation.mkd | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 doc/hook-propagation.mkd diff --git a/doc/hook-propagation.mkd b/doc/hook-propagation.mkd new file mode 100644 index 0000000..cda9100 --- /dev/null +++ b/doc/hook-propagation.mkd @@ -0,0 +1,44 @@ +# hook propagation in gitolite + +There have been some questions about how hooks propagate, and when. I'll try +and set out the logic here. + +First: realise that gitolite *wants to make sure* that all the hooks in your +`hooks/common` directory get copied (symlinked, actually) to *every* repo that +gets created. **Not doing so is generally a security risk; because the +primary purpose of gitolite is access control, people generally *want* hooks +to run.** + +Here's how/when hooks are created/propagated: + +1. anytime you do an install, gitolite trawls through *all* existing repos + (using the unix `find` command) and force-links all the hooks in all the + repos so they all get the latest and greatest hooks. + +2. anytime you do a "compile" (meaning push changes to the admin repo), + gitolite looks through all the repos named in the config. It first checks + if the repo exists, creating it if needed. It then looks for a sentinel + file called "gitolite-hooked" (an empty file in the hooks directory). If + it doesn't find it, it will assume that hooks need to be propagated. + + This is because people often copy a repo from elsewhere, add it to the + config, and expect things to work. Without this step, those repos don't + get the hooks, which is bad -- the access control would have failed + silently! + +3. anytime a new repo is created, the same force-linking of hooks happens. + The 3 places a new repo is created are: + + * the "compile" case mentioned above, where the admin added a normal + repo to the config and pushed + + * the wildrepos case, where you have "C" permissions and the repo does + not already exist + + * the `fork` command in `contrib/adc`. In this case the hooks are + explicitly copied from the source repo using the `cp` command, not + using the code internal to gitolite. + +For people who do not want certain hooks to run for certain repos, one simple +solution that will work right now is to check the value of `$GL_REPO` at the +start of the hook, and `exit 0` based on what it contains/matches. From 98a42be614e4999c426fa10c02a16250a025d63d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 19 Jun 2010 12:09:04 +0530 Subject: [PATCH 400/850] asking for other users' perms had a bug in BIG_CONFIG mode fixes: - allow "grouped" admins to get basic info for other users by checking more than just the *user*'s right to the admin repo - report_basic is called with a $user argument, but it's not easy (right now) to propagate this to parse_acl. Use a simple kludge, (for now at least). thanks to bcooksley for catching this --- src/gitolite.pm | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 13d1451..4f6d3c9 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -327,6 +327,12 @@ sub report_basic { my($GL_ADMINDIR, $GL_CONF_COMPILED, $user) = @_; + # XXX The correct way is actually to give parse_acl another argument + # (defaulting to $ENV{GL_USER}, the value being used now). But for now + # this will do, even though it's a bit of a kludge to get the basic access + # rights for some other user this way + local $ENV{GL_USER} = $user; + &parse_acl($GL_CONF_COMPILED, "", "CREATOR", "READERS", "WRITERS"); # send back some useful info if no command was given @@ -469,8 +475,11 @@ sub special_cmd print "you also have shell access\r\n" if $shell_allowed; } elsif ($cmd =~ /^info\s+(.+)$/) { my @otherusers = split ' ', $1; + + my($perm, $creator, $wild) = &repo_rights('gitolite-admin'); + die "you can't ask for others' permissions\n" unless $perm =~ /W/; + &parse_acl($GL_CONF_COMPILED); - die "you can't ask for others' permissions\n" unless $repos{'gitolite-admin'}{'R'}{$user}; for my $otheruser (@otherusers) { warn("ignoring illegal username $otheruser\n"), next unless $otheruser =~ $USERNAME_PATT; &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $otheruser); From acff0bd3d04f481aa3ed50bbad33c382bf4e2059 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 20 Jun 2010 08:06:54 +0530 Subject: [PATCH 401/850] hook propagation doc much expanded now --- doc/hook-propagation.mkd | 135 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 2 deletions(-) diff --git a/doc/hook-propagation.mkd b/doc/hook-propagation.mkd index cda9100..44ef8da 100644 --- a/doc/hook-propagation.mkd +++ b/doc/hook-propagation.mkd @@ -1,7 +1,135 @@ # hook propagation in gitolite -There have been some questions about how hooks propagate, and when. I'll try -and set out the logic here. +Advanced users need to know how hooks propagate, and when. They also need to +know where to place their hooks, and since there appear to be two places to +put them, what takes precedence. I'll try and set out the logic here. + +In this document: + + * hooks used by gitolite + * **where** do I (the admin) put the hooks? + * the "from-client" method + * the other 3 methods + * the `GL_PACKAGE_HOOKS` directory + * the `$HOME/.gitolite` directory + * why two places? + * special case: the "non-root" method + * **when** do hooks propagate? + + + +### hooks used by gitolite + +Gitolite uses only 2 hooks. **All** repos have an `update` hook, without +which there is no write-level access control (per-branch permissions). The +special **gitolite-admin** repo has a special `post-update` hook, which is +required to do its, umm, special things, like running the "compile" script, +etc. + +In addition there is a "sentinel file" -- an empty file called +"gitolite-hooked". We'll see later what this does. + +The final objective of all this is that each repo's `hooks/` directory should +get all the hooks that it is meant to get. + + + +### **where** do I (the admin) put the hooks? + +In general, **all** hooks go into the `hooks/common` directory. Only the +special `post-update` hook meant for the admin repo goes into +`hooks/gitolite-admin`. + +Now we'll discuss the locations of these `hooks/common` and +`hooks/gitolite-admin` directories. This depends on which install method you +used. + +(Please refer to [doc/0-INSTALL.mkd][0inst] for what these "methods" are). + + + +#### the "from-client" method + +Let's get this out of the way first, because it is simple: if you're using the +"from-client" method, there's only one place: the `hooks` directory in your +gitolite clone on the client side. This is where you run +`src/gl-easy-install` from. Nothing else in this section is relevant to this +method; skip to the next section ("when do hooks propagate") if you installed +using the "from-client" method. + + + +#### the other 3 methods + + + +##### the `GL_PACKAGE_HOOKS` directory + +You might recall that the "root", and "non-root" methods run a command called +`gl-system-install`, the third argument of which is some directory of your +choice (like maybe `/usr/share/gitolite/hooks`). Even though it is not +necessary to know this, internally this becomes the value of the +`$GL_PACKAGE_HOOKS` variable, so in this document we will refer to that +variable instead of the location (because you might choose any location you +like for it). + +The "package" method also has the same property, except that the packager has +already decided what that location is, and the package creation/install +process does the equivalent of `gl-system-install`. + +So now we know there's a location called `$GL_PACKAGE_HOOKS` where you can +place your hooks. + + + +##### the `$HOME/.gitolite` directory + +You might also recall that, in these three methods, each **hosting user** has +to run `gl-setup`. This sets up, among other things, `$HOME/.gitolite` +directory, which also contains a `hooks/` directory. + +So now there are two places you can put your hooks, apparently. + + + +#### why two places? + +Just think of the "package" and "root" methods for now, even if you're using +the "non-root" method. + +In these two methods, it is reasonable to assume that the entire site (or +server) has certain policies that they want to implement using hooks. They +want to enforce these hooks on *each hosting user*. These hooks go into +`$GL_PACKAGE_HOOKS`. + +Each hosting user then has the discretion to add his own hooks (modulo name +clashes, which may necessitate hook chaining, etc., like we already do for the +hooks that gitolite cares about). He adds these hooks to his +`$HOME/.gitolite/hooks` directory. + +When hooks propagate, the ones in `$GL_PACKAGE_HOOKS` override/overwrite the +ones in `$HOME/.gitolite/hooks`. Otherwise it wouldn't make sense; you +wouldn't be able to enforce site-wide hooks. + +[NOTE: due to a minor quirk, the site-wide hooks in `$GL_PACKAGE_HOOKS` also +get copied to `$HOME/.gitolite/hooks` when you "install". I need to fix and +thoroughly test this later; for now, just ignore the extra files you see in +there; they're harmless/redundant (TODO)] + + + +#### special case: the "non-root" method + +This method was created later, just piggy-backing on everything that already +existed to cater to the "package" and "root" methods. In this method, the +`$GL_PACKAGE_HOOKS` is as accessible or under your control as +`$HOME/.gitolite`, so it doesn't matter where you put your hooks. I +*strongly* suggest putting them in `$GL_PACKAGE_HOOKS` and ignoring +`$HOME/.gitolite` completely. + + + +### **when** do hooks propagate? First: realise that gitolite *wants to make sure* that all the hooks in your `hooks/common` directory get copied (symlinked, actually) to *every* repo that @@ -42,3 +170,6 @@ Here's how/when hooks are created/propagated: For people who do not want certain hooks to run for certain repos, one simple solution that will work right now is to check the value of `$GL_REPO` at the start of the hook, and `exit 0` based on what it contains/matches. + +[0inst]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd + From d6a7e3b18211e5f437ebe46b9228bb7ef1dd4658 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 22 Jun 2010 14:52:05 +0530 Subject: [PATCH 402/850] (minor) install doc, new hooks --- doc/0-INSTALL.mkd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 6326641..22832a2 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -222,6 +222,9 @@ marked "(U)". Also, if you're using either of the two methods that use the `src/gl-system-install` command, please make sure you give it the same arguments! +If you've added any new hooks, please also run the next step (`gl-setup`) +also. + Also, remember that some new features may require additional settings in your `~/.gitolite.rc` file. From 38403c354fd3499893e5d3f0f4881a21aa783dcd Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 22 Jun 2010 07:30:48 -0400 Subject: [PATCH 403/850] Add GL_WILDREPOS_DEFPERMS allows a default 'setperms' string to be set for new wildcard repositories. Also, fix a bug in the fork script where a failure in the git command would still cause the rest of the script to attempt to run. --- conf/example.gitolite.rc | 10 ++++++++++ contrib/adc/fork | 6 ++++++ src/gl-auth-command | 5 ++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index d7b18d8..24e52a5 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -189,6 +189,16 @@ $SVNSERVE = ""; $GL_WILDREPOS = 0; +# -------------------------------------- +# DEFAULT WILDCARD PERMISSIONS + +# If set, this value will be used as the default user-level permission rule of +# new wildcard repositories. The user can change this value with the setperms command +# as desired after repository creation; it is only a default. Note that @all can be +# used here but is special; no other groups can be used in user-level permissions. + +# $GL_WILDREPOS_DEFPERMS = 'R = @all'; + # -------------------------------------- # HOOK CHAINING diff --git a/contrib/adc/fork b/contrib/adc/fork index b61f415..3baab01 100755 --- a/contrib/adc/fork +++ b/contrib/adc/fork @@ -13,8 +13,14 @@ get_rights_and_owner $to # clone $from to $to git clone --bare -l $GL_REPO_BASE_ABS/$from.git $GL_REPO_BASE_ABS/$to.git +[ $? -ne 0 ] && exit 1 # fix up creator, and hooks cd $GL_REPO_BASE_ABS/$to.git echo $GL_USER > gl-creater cp -R $GL_REPO_BASE_ABS/$from.git/hooks/* $GL_REPO_BASE_ABS/$to.git/hooks + +if [ -n "$GL_WILDREPOS_DEFPERMS" ]; then + echo "$GL_WILDREPOS_DEFPERMS" > gl-perms +fi + diff --git a/src/gl-auth-command b/src/gl-auth-command index 8ca43d9..a161dca 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -23,7 +23,7 @@ use warnings; # ---------------------------------------------------------------------------- # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_ADC_PATH, $SVNSERVE); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; @@ -46,6 +46,9 @@ $ENV{GL_BINDIR} = $bindir; # add a custom path for git binaries, if specified $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; +# set default permission of wildcard repositories +$ENV{GL_WILDREPOS_DEFPERMS} = $GL_WILDREPOS_DEFPERMS if $GL_WILDREPOS_DEFPERMS; + # set the umask before creating any files umask($REPO_UMASK); From 14248a34415fea678a16e1212f71bbe0db213974 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 22 Jun 2010 15:11:50 +0000 Subject: [PATCH 404/850] add '[' as allowed starting character for repo patterns this allows the first part of the repo name (if wildcard repos are activated) to have a regex like [a-zA-Z0-9]+. ---- Note added by committer: he assumption used to be that all wildcard repos will have some common prefix like "users", but I did not imagine it would be like repo [a-zA-Z0-9]+/users/CREATOR/[a-zA-Z0-9]+ (viz., the "users" is in the middle). Sounds reasonable... --- src/gitolite.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 4f6d3c9..f432074 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -33,8 +33,8 @@ our $W_COMMANDS=qr/^git[ -]receive-pack$/; # because in this version, a repo can have "CREATOR" in the name (see docs) our $REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$); # very simple pattern our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple pattern -# same as REPONAME, plus some common regex metas -our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); +# same as REPONAME, but used for wildcard repos, allows some common regex metas +our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG); From 148897341296dd1de5705336f7fd1494b9b1d828 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 5 Jul 2010 15:54:44 +0530 Subject: [PATCH 405/850] (doc) INSTALL doc needs info on URLs More and more people are using one of the first 3 methods of install (the ones that don't involve running "src/gl-easy-install" from the client side) usualy due to RPM/DEB being available now. Previously, the ending message on running that command was serving this purpose, and so it never really got written down in so many words. [thanks to antgel for catching this] ---- while we were there, we removed a now-obsolete section that talks about how to use just one key; there are better methods now --- doc/0-INSTALL.mkd | 18 ++++++++++++- doc/6-ssh-troubleshooting.mkd | 51 +++++------------------------------ 2 files changed, 23 insertions(+), 46 deletions(-) diff --git a/doc/0-INSTALL.mkd b/doc/0-INSTALL.mkd index 22832a2..8c0f2f4 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/0-INSTALL.mkd @@ -13,6 +13,7 @@ In this document: * (root method) directly on the server, manually, with root access * (non-root method) directly on the server, manually, without root access * (from-client method) install from the client to the server + * URLs for gitolite-managed repos * special cases -- multiple gitolite servers * package method and root method * from-client method @@ -165,6 +166,21 @@ documentation. This method is verbosely documented in [doc/7-install-transcript.mkd][doc7], including *outputs* of the commands concerned. + + +### URLs for gitolite-managed repos + +The URL for normal users (i.e., users other than the admin) is always of the +form "git@server:reponame". So, for instance, `git clone git@server:testing` +gets any valid user a copy of the "testing" repo. + +In the first 3 install methods, the admin user will also use the same URL +format, like `git clone git@server:gitolite-admin`. + +However, in the fourth ("from-client") method, the admin user needs a +different URL (`gitolite:reponame`) to gain access to the gitolite +repositories. Check [here][twokeys] for why. + ### special cases -- multiple gitolite servers @@ -264,4 +280,4 @@ uninstalling gitolite is described in great detail in [doc7]: http://github.com/sitaramc/gitolite/blob/pu/doc/7-install-transcript.mkd [doc9unin]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-uninstall.mkd [doc9ssh]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-ssh-tips.mkd - +[twokeys]: http://github.com/sitaramc/gitolite/blob/pu/doc/6-ssh-troubleshooting.mkd#why_two_keys_on_client diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 8fd4edd..39ba275 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -377,53 +377,13 @@ them; the ssh server will always (*always*) pick the first one in sequence when the key is offered by the ssh client. So the next question is usually "I have other ways to get a shell on that -account, so why do I need a key for shell access at all?" +account (like `su - git` from some other account), so why do I need a key for +shell access at all?" The answer to this is that the "easy install" script, being written for the -most general case, needs shell access via ssh to do its stuff. - -If you really, really, want to get rid of the extra key, here's a transcript -that should have enough info to get you going (but it helps to know ssh well): - - * on "sitaram" user, on my workstation - - cd ~/.ssh - cp id_rsa sitaram - cp id_rsa.pub sitaram.pub - cd ~/gitolite-clone - src/gl-easy-install -q git my.git.server sitaram - - that last command produces something like the following: - - you are upgrading from (unknown) to v0.80-6-gdde8c4e - setting up keypair... - ...reusing /home/sitaram/.ssh/sitaram.pub... - creating gitolite para in ~/.ssh/config... - finding/creating gitolite rc... - installing/upgrading... - Pseudo-terminal will not be allocated because stdin is not a terminal. - [master (root-commit) e717a89] start - 2 files changed, 11 insertions(+), 0 deletions(-) - create mode 100644 conf/gitolite.conf - create mode 100644 keydir/sitaram.pub - cloning gitolite-admin repo... - Initialized empty Git repository in /home/sitaram/gitolite-admin/.git/ - fatal: 'gitolite-admin.git' does not appear to be a git repository - fatal: The remote end hung up unexpectedly - - notice that the final step (the clone of the newly created gitolite-admin - repo) failed, as expected - - * now log on to the git hosting account (`git@my.git.server` in this - example), edit `~/.ssh/authorized_keys`, and delete the line with the - first occurrence of your key (this should be *before* the `# gitolite - start` line) - - * now go back to your workstation and - - git clone git@my.git.server:gitolite-admin - -That should do it. +most general case, needs shell access via ssh to do its stuff. If you have +access otherwise, you really should use one of the other 3 install methods to +install gitolite. Please see the [install doc][install] for details. @@ -494,3 +454,4 @@ bigger problems than gitolite install not working!)] [doc0]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd [doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-gitolite-and-ssh.mkd +[install]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd From 20c2e1aac8aa4dfded5b280cc5f98d996ac5d3b9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 6 Jul 2010 05:50:44 +0530 Subject: [PATCH 406/850] there are people who cannot click a "doc/" link on the first google hit, and think you have to clone the whole thing to see the docs... I suspect they aren't even hitting the "read more" link that github shows in the description blurb, or if they go there they aren't even going to the end of the second para, which contains a nice link. --- README.mkd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.mkd b/README.mkd index b4c0760..b88d0e5 100644 --- a/README.mkd +++ b/README.mkd @@ -4,6 +4,12 @@ Gitolite is an access control layer on top of git, which allows access control down to the branch level, including specifying who can and cannot *rewind* a given branch. +This README will give you a quick intro. All documentation is available +within the source repo, in markdown format. If you want to read it *without* +cloning the source repo, just hit [this link][docs] and read nicely HTML-ised +versions of all the docs (automatic rendering of markdown to HTML courtesy +github). + ---- In this document: From bdb7cd690397e50f7c1a1c9c7a15583e9ad148cf Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 21 Jul 2010 06:27:43 +0530 Subject: [PATCH 407/850] move empty ARGV check down after "-s" check to make calling "gl-auth -s" (no username) work --- src/gl-auth-command | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index a161dca..e9ab1b0 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -58,18 +58,18 @@ my $repo_base_abs = $ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE # start... # ---------------------------------------------------------------------------- -# no arguments given? default user is $USER (fedorahosted works like this, -# and it is harmless for others) -@ARGV = ($ENV{USER}) unless @ARGV; - # if the first argument is a "-s", this user is allowed to get a shell using # this key my $shell_allowed = 0; -if ($ARGV[0] eq '-s') { +if (@ARGV and $ARGV[0] eq '-s') { $shell_allowed = 1; shift; } +# no (more) arguments given? default user is $USER (fedorahosted works like +# this, and it is harmless for others) +@ARGV = ($ENV{USER}) unless @ARGV; + # first, fix the biggest gripe I have with gitosis, a 1-line change my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! From e9787b03f8d9812c0b46b024499ee94533e164fe Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 16 Jul 2010 11:25:31 +0530 Subject: [PATCH 408/850] darn... git 1.7+ puts out different messages on clone... --- t/t00-initial | 2 +- t/t03a-branch-permissions | 4 ++-- t/t04a-wild-all | 8 ++++---- t/t04a-wild-students | 8 ++++---- t/t09a-oldtests | 2 +- t/t50-sequence-test | 6 +++--- t/t53-check-info-expand-output | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/t/t00-initial b/t/t00-initial index d708fdd..c17ee86 100644 --- a/t/t00-initial +++ b/t/t00-initial @@ -19,7 +19,7 @@ expect "remote: Initialized empty Git repository in /home/gitolite-test/reposito name "basic clone" cd ~/td runlocal git clone u1:aa u1aa -expect "Initialized empty Git repository in /home/tester/td/u1aa/.git/" +expect "Cloning into u1aa..." expect "warning: You appear to have cloned an empty repository" runlocal ls -ald u1aa expect "drwxr-xr-x 3 $USER $USER 4096 201.-..-.. ..:.. u1aa" diff --git a/t/t03a-branch-permissions b/t/t03a-branch-permissions index 2d7c6e4..f8a3452 100644 --- a/t/t03a-branch-permissions +++ b/t/t03a-branch-permissions @@ -142,7 +142,7 @@ repo r1 notexpect ABORT cd ~/td runlocal git clone gitolite:r1 -expect "Initialized empty Git repository in /home/tester/td/r1/.git/" +expect "Cloning into r1..." cd ~/td/r1 mdc; mdc; mdc mdc; mdc; mdc @@ -163,7 +163,7 @@ repo r2 notexpect ABORT cd ~/td runlocal git clone gitolite:r2 -expect "Initialized empty Git repository in /home/tester/td/r2/.git/" +expect "Cloning into r2..." cd ~/td/r2 mdc; mdc; mdc mdc; mdc; mdc diff --git a/t/t04a-wild-all b/t/t04a-wild-all index ae9010f..7bcefab 100644 --- a/t/t04a-wild-all +++ b/t/t04a-wild-all @@ -52,7 +52,7 @@ expect "\* \[new branch\] master -> master" name "u1 clone success" cd ~/td runlocal git clone u1:foo/u4/a12 u1a12 -expect "Initialized empty Git repository in /home/tester/td/u1a12/.git/" +expect "Cloning into u1a12..." name "u1 push fail" cd ~/td/u1a12 @@ -63,7 +63,7 @@ expect "W access for foo/u4/a12 DENIED to u1" name "u2 clone success" cd ~/td runlocal git clone u2:foo/u4/a12 u2a12 -expect "Initialized empty Git repository in /home/tester/td/u2a12/.git/" +expect "Cloning into u2a12..." name "u2 push success" cd ~/td/u2a12 @@ -113,7 +113,7 @@ expect_filesame $TESTDIR/out/t04-wild1.2 name "u5 clone success" cd ~/td runlocal git clone u5:foo/u4/a12 u5a12 -expect "Initialized empty Git repository in /home/tester/td/u5a12/.git/" +expect "Cloning into u5a12..." name "u5 push fail" cd ~/td/u5a12 @@ -124,7 +124,7 @@ expect "W access for foo/u4/a12 DENIED to u5" name "u6 clone success" cd ~/td runlocal git clone u6:foo/u4/a12 u6a12 -expect "Initialized empty Git repository in /home/tester/td/u6a12/.git/" +expect "Cloning into u6a12..." name "u6 push success" cd ~/td/u6a12 diff --git a/t/t04a-wild-students b/t/t04a-wild-students index a37a6ab..0fdc85c 100644 --- a/t/t04a-wild-students +++ b/t/t04a-wild-students @@ -50,7 +50,7 @@ expect "\* \[new branch\] master -> master" name "u1 clone success" cd ~/td runlocal git clone u1:foo/u4/a12 u1a12 -expect "Initialized empty Git repository in /home/tester/td/u1a12/.git/" +expect "Cloning into u1a12..." name "u1 push fail" cd ~/td/u1a12 @@ -61,7 +61,7 @@ expect "W access for foo/u4/a12 DENIED to u1" name "u2 clone success" cd ~/td runlocal git clone u2:foo/u4/a12 u2a12 -expect "Initialized empty Git repository in /home/tester/td/u2a12/.git/" +expect "Cloning into u2a12..." name "u2 push success" cd ~/td/u2a12 @@ -111,7 +111,7 @@ expect_filesame $TESTDIR/out/t04-wild1.2 name "u5 clone success" cd ~/td runlocal git clone u5:foo/u4/a12 u5a12 -expect "Initialized empty Git repository in /home/tester/td/u5a12/.git/" +expect "Cloning into u5a12..." name "u5 push fail" cd ~/td/u5a12 @@ -122,7 +122,7 @@ expect "W access for foo/u4/a12 DENIED to u5" name "u6 clone success" cd ~/td runlocal git clone u6:foo/u4/a12 u6a12 -expect "Initialized empty Git repository in /home/tester/td/u6a12/.git/" +expect "Cloning into u6a12..." name "u6 push success" cd ~/td/u6a12 diff --git a/t/t09a-oldtests b/t/t09a-oldtests index 16edb61..5e6e178 100644 --- a/t/t09a-oldtests +++ b/t/t09a-oldtests @@ -36,7 +36,7 @@ notexpect "failed to push" cd ~/td runlocal git clone u1:abc -expect "Initialized empty Git repository in /home/tester/td/abc/.git/" +expect "Cloning into abc..." cd ~/td/abc mdc jfile; runlocal git push origin master expect "To u1:abc" diff --git a/t/t50-sequence-test b/t/t50-sequence-test index 4c540f7..0a33eb7 100644 --- a/t/t50-sequence-test +++ b/t/t50-sequence-test @@ -37,7 +37,7 @@ do name "push" cd ~/td runlocal git clone u2:foo/u1/bar u2bar - expect "Initialized empty Git repository in /home/tester/td/u2bar/.git/" + expect "Cloning into u2bar..." cd u2bar mdc u2file1 runlocal git push @@ -66,7 +66,7 @@ do cd ~/td runlocal git clone u1:foo/u1/bar - expect "Initialized empty Git repository in /home/gitolite-test/repositories/foo/u1/bar.git/" + expect "Cloning into bar..." cd bar mdc u1file1 @@ -83,7 +83,7 @@ do name "push" cd ~/td runlocal git clone u2:foo/u1/bar u2bar - expect "Initialized empty Git repository in /home/tester/td/u2bar/.git/" + expect "Cloning into u2bar..." cd u2bar mdc u2file1 runlocal git push diff --git a/t/t53-check-info-expand-output b/t/t53-check-info-expand-output index 43bb938..ad845b7 100644 --- a/t/t53-check-info-expand-output +++ b/t/t53-check-info-expand-output @@ -34,14 +34,14 @@ do name "u1 info" runlocal ssh u1 info - expect "hello u1, the gitolite version here is v" + expect "hello u1, the gitolite version here is" expect "@R.*@W.*testing" [ "$wr" = "1" ] && expect "C.*R.*W.*foo/u1/\\.\\+" name "u1 expand" runlocal ssh u1 expand [ "$wr" = "0" ] && expect "wildrepos disabled, sorry" - [ "$wr" = "1" ] && expect "hello u1, the gitolite version here is v" + [ "$wr" = "1" ] && expect "hello u1, the gitolite version here is" [ "$wr" = "1" ] && expect "R.*W.*(u1).*foo/u1/bar" [ "$wr" = "1" ] && expect "@R.*@W.*testing" From 988934dec1b631d95fe9bc730182475b195edbd7 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 23 Jul 2010 16:11:44 +0530 Subject: [PATCH 409/850] (minor) admin doc change on moving existing repos into gitolite --- doc/2-admin.mkd | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 6c0b036..9b2a7be 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -66,12 +66,23 @@ Once you've cloned it, you're ready to add users and repos. #### moving pre-existing repos into gitolite One simple way to add a pre-existing repo to gitolite is to let gitolite -create it as a brand new repo as in the previous section, and then, from an -existing clone, "push --all" to the new one. +create it as a brand new repo as in the previous section, then do the +following: -However, if you have many existing repos to add, this can be time-consuming -and error-prone. Here's how to take a bunch of existing repos and add them to -gitolite: + cd your-copy-of-the-repo + # make sure all the branches are correct and no extra stuff, "temp" + # branches, etc., are present + git push --all git@server:reponame + git push --tags git@server:reponame + +(You could also use "git push --mirror" instead of separately doing branches +and tags, but that will carry across *your* remote refs also, and typically +you may not want that. Anyway please do a `git ls-remote git@server:repo` to +make sure all the stuff you want went through, and is named correctly). + +All this is actually very simple and easily done. However, if you have many +existing repos to add, this can be time-consuming and error-prone. Here's how +to take a bunch of existing repos and add them to gitolite: * make sure they're *bare* repos ;-) From 0d0e7e5d72cec6e839716207a7d33b777cc9754b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 23 Jul 2010 17:03:21 +0530 Subject: [PATCH 410/850] (really big config) 2 new rc variables ...to prevent auto-creation of repos from config, and to prevent processing of ssh keys. Also doc update --- conf/example.gitolite.rc | 2 ++ doc/big-config.mkd | 42 +++++++++++++++++++++++++++------------- src/gl-compile-conf | 35 ++++++++++++++++++++++++++++----- 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 24e52a5..272d618 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -89,6 +89,8 @@ $GIT_PATH=""; $GL_BIG_CONFIG = 0; $GL_NO_DAEMON_NO_GITWEB = 0; +$GL_NO_CREATE_REPOS = 0; +$GL_NO_SETUP_AUTHKEYS = 0; # ---------------------------------------------------------------------- # SECURITY SENSITIVE SETTINGS diff --git a/doc/big-config.mkd b/doc/big-config.mkd index 278c152..c70b866 100644 --- a/doc/big-config.mkd +++ b/doc/big-config.mkd @@ -4,7 +4,7 @@ In this document: * when/why do we need it? * how do we use it? - * summary of settings in RC file + * other optimisations * what are the downsides? * (extra coolness) usergroups and LDAP/similar tools @@ -107,8 +107,9 @@ Just set $GL_BIG_CONFIG = 1; -in the `~/.gitolite.rc` file on the server. When you do that, and push this -configuration, the compiled file looks like this: +in the `~/.gitolite.rc` file on the server (see next section for more +variables). When you do that, and push this configuration, the compiled file +looks like this: %repos = ( '@wbr' => { @@ -142,23 +143,38 @@ configuration, the compiled file looks like this: That's a lot smaller, and allows orders of magintude more repos and groups to be supported. - + -### summary of settings in RC file +### other optimisations -The default RC file contains the following lines: +The default RC file contains the following lines (we've already discussed the +first one): $GL_BIG_CONFIG = 0; $GL_NO_DAEMON_NO_GITWEB = 0; + $GL_NO_CREATE_REPOS = 0; + $GL_NO_SETUP_AUTHKEYS = 0; -The first setting means that by default, big-config is off; you can change it -to 1 to enable it. +`GL_NO_DAEMON_NO_GITWEB` is a very useful optimisation that you *must* enable +if you *do* have a large number of repositories, and do *not* use gitolite's +support for gitweb or git-daemon access (see "[easier to specify gitweb +description and gitweb/daemon access][gw]" for details). This will save a lot +of time when you push the gitolite-admin repo with changes. This variable +also control whether "git config" lines (such as `config hooks.emailprefix = +"[gitolite]"`) will be processed or not. -The second is a very useful optimisation that you *must* enable if you *do* -have a large number of repositories, and do *not* use gitolite's support for -gitweb or git-daemon access (see "[easier to specify gitweb description and -gitweb/daemon access][gw]" for details). This will save a lot of time when -you push the gitolite-admin repo with changes. +Setting this is relatively harmless to a normal installation, unlike the next +two variables :-) `GL_NO_CREATE_REPOS` and `GL_NO_SETUP_AUTHKEYS` are meant +for installations where some backend system already exists that does all the +actual repo creation, and all the authentication setup (ssh auth keys), +respectively. + +Summary: Please **leave those two variables alone** unless you're initials are +"JK" ;-) + +Also note that using all 3 of the `GL_NO_*` variables will result in +*everything* after the config compile being skipped. In other words, gitolite +is being used **only** for its access control language. [gw]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gitweb diff --git a/src/gl-compile-conf b/src/gl-compile-conf index f0b27fa..d9577c8 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB, $GL_NO_CREATE_REPOS, $GL_NO_SETUP_AUTHKEYS); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -395,12 +395,15 @@ print $compiled_fh Data::Dumper->Dump([\%groups], [qw(*groups)]) if $GL_BIG_CONF close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; # ---------------------------------------------------------------------------- -# any new repos to be created? +# (that ends the config file compiler and write) # ---------------------------------------------------------------------------- -# modern gits allow cloning from an empty repo, so we just create it +# ---------------------------------------------------------------------------- +# what's the git version? +# ---------------------------------------------------------------------------- + +# we don't like stuff older than 1.6.2 -# but it turns out not everyone has "modern" gits :) my $git_version = `git --version`; die " *** ERROR *** @@ -418,11 +421,22 @@ die "\n\t\t***** AAARGH! *****\n" . "\tthe newer features, please upgrade.\n" if $git_version < 10602; # that's 1.6.2 to you + + +# ---------------------------------------------------------------------------- +# the rest of this program can be "switched off"; see doc/big-config.mkd for +# details. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# any new repos to be created? +# ---------------------------------------------------------------------------- + # repo-base needs to be an absolute path for this loop to work right # so if it was not already absolute, prefix $HOME. my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); -{ +unless ($GL_NO_CREATE_REPOS) { wrap_chdir("$repo_base_abs"); # autocreate repos. Start with the ones that are normal repos in %repos @@ -460,6 +474,10 @@ my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" # update repo configurations # ---------------------------------------------------------------------------- +# no gating required for this. If you don't have any "config" lines it won't +# run anyway. An example of a config line could be: +# config hooks.emailprefix = "[foo]" + for my $repo (keys %repo_config) { wrap_chdir("$repo_base_abs/$repo.git"); while ( my ($key, $value) = each(%{ $repo_config{$repo} }) ) { @@ -540,6 +558,9 @@ unless ($GL_NO_DAEMON_NO_GITWEB) { # "compile" ssh authorized_keys # ---------------------------------------------------------------------------- +# NOTE: for now we assume that setting up authkeys is the LAST thing we do! +exit 0 if $GL_NO_SETUP_AUTHKEYS; + my $authkeys_fh = wrap_open( "<", $ENV{HOME} . "/.ssh/authorized_keys", "\tFor security reasons, gitolite will not *create* this file if it does\n" . "\tnot already exist. Please see the \"admin\" document for details\n"); @@ -620,3 +641,7 @@ system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys") and die "couldn't write authkeys file\n"; system("rm $ENV{HOME}/.ssh/new_authkeys"); + +# NOTE: if you're adding code here that is unrelated to setting up authkeys, +# remember that control may not reach here if a sysadm has set +# GL_NO_SETUP_AUTHKEYS in the rc file. From 063d2f9c6e6dc5a23a16d1f2bb4506349e904b47 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 29 Jul 2010 09:05:13 +0530 Subject: [PATCH 411/850] (minor) don't assume "ssh gitolite" applicable always in docs [thanks to hal-eisen on github for catching this, although I could not directly use his patches.] --- doc/3-faq-tips-etc.mkd | 3 +++ doc/4-wildcard-repositories.mkd | 3 +++ doc/6-ssh-troubleshooting.mkd | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 935e4b3..345ac77 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -458,7 +458,10 @@ Easy! Just use ssh to give the "info" command to the gitolite server: To understand what these symbols mean, please see doc/report-output.mkd. The administrator can also say things like: + # if you installed using the "from-client" method ssh gitolite info u1 u2 u3 + # for the other 3 install methods + ssh git@server info u1 u2 u3 to get this info for other user(s). diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index f5a66f0..5addbfa 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -213,7 +213,10 @@ file: Push the config, then try + # if you installed using the "from-client" method ssh gitolite expand + # for the other 3 install methods + ssh git@server expand diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 39ba275..636907c 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -33,6 +33,11 @@ In addition to both these documents, there's now a program called `sshkeys-lint` that you can run on your client. Run it without arguments to get help on how to run it and what inputs it needs. +**IMPORTANT NOTE: A lot of this document applies only if you installed using +the [from-client method][fc] . If you used one of the [other 3 methods][o3] , +some of it may or may not apply. At the very least, any mention of `ssh +gitolite` below will change to `ssh git@server`; see [this][urls] for why.** + ### the most common problems that an admin will see @@ -455,3 +460,6 @@ bigger problems than gitolite install not working!)] [doc0]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd [doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-gitolite-and-ssh.mkd [install]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd +[o3]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#installation_and_setup +[fc]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#from_client_method_install_from_the_client_to_the_server +[urls]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#URLs_for_gitolite_managed_repos From adbafdfc2d3a904844dd17b7a852a1c15f9ad99e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 30 Jul 2010 08:47:12 +0530 Subject: [PATCH 412/850] (minor) updated fedora test case to add new RC vars introduced 2 commits in HEAD^^ --- t/t-fedora-big-config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/t-fedora-big-config b/t/t-fedora-big-config index 74c9ca2..7d4fd53 100644 --- a/t/t-fedora-big-config +++ b/t/t-fedora-big-config @@ -3,6 +3,8 @@ cd $TESTDIR $TESTDIR/rollback || die "rollback failed" editrc GL_BIG_CONFIG 1 editrc GL_NO_DAEMON_NO_GITWEB 1 +editrc GL_NO_CREATE_REPOS 1 +editrc GL_NO_SETUP_AUTHKEYS 1 catrc expect "GL_BIG_CONFIG *= *1;" From c25e05d87b630b0472411202165e2c2d5e2bed25 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 30 Jul 2010 05:44:29 +0530 Subject: [PATCH 413/850] distinguish "repo not found" from "no access" if the user has rights anyway thanks to Jesse from the Fedora team for pointing this out. They use GL_NO_CREATE_REPOS, so sometimes the physical repo on disk doesn't exist at the time the config file is written. We're talking about non-wild repos only here, so this means it should never happen to normal gitolite users. But now -- in the rare case that there is a disk-side problem -- people who have rights to a repo will get a more specific error message. --- src/gitolite.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index f432074..dbcfcfc 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -437,7 +437,7 @@ sub expand_wild $perm = ( $repos{$repo}{C}{'@all'} ? ' @C' : ( $repos{$repo}{C}{$ENV{GL_USER}} ? ' =C' : ' ' )) if $GL_WILDREPOS; # if you didn't have perms to create it, delete the "convenience" # copy of the ACL that parse_acl makes - delete $repos{$repo} unless $perm =~ /C/; + delete $repos{$repo} if $perm !~ /C/ and $wild; $creator = ""; } $perm .= ( $repos{$repo}{R}{'@all'} ? ' @R' : ( $repos{'@all'}{R}{$ENV{GL_USER}} ? ' #R' : ( $repos{$repo}{R}{$ENV{GL_USER}} ? ' R' : ' ' ))); From cd0835feb15e9f2223edad73035a4d38570a315a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 30 Jul 2010 13:50:51 +0530 Subject: [PATCH 414/850] (tests) t59 to test previous commit... (the one that distinguishes "repo not found" from "no access") --- t/t59-repo-not-on-disk | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 t/t59-repo-not-on-disk diff --git a/t/t59-repo-not-on-disk b/t/t59-repo-not-on-disk new file mode 100644 index 0000000..2873763 --- /dev/null +++ b/t/t59-repo-not-on-disk @@ -0,0 +1,114 @@ +# vim: syn=sh: +# check basic repo level permissions, including what happens when the repo on +# disk is missing (non-wildcard only) + +for bc in 0 1 +do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_BIG_CONFIG $bc + + name "INTERNAL" + echo " + @g1 = u1 u2 + @g2 = u3 u4 + @g3 = u5 u6 + repo aa + RW+ = tester + RW = @g1 + R = @g2 + " | ugc + expect_push_ok "master -> master" + expect "remote: Initialized empty Git repository in /home/gitolite-test/repositories/aa.git/" + + cd ~/td + name "check u1 can auto-create and push" + runlocal git clone u1:aa + expect "Cloning into aa..." + expect "warning: You appear to have cloned an empty repository" + cd ~/td/aa + mdc; mdc; mdc + mdc; mdc; mdc + mdc; mdc; mdc + name "INTERNAL" + runlocal git push origin HEAD + expect "To u1:aa" + expect "\* \[new branch\] HEAD -> master" + expect_push_ok "HEAD -> master" + + name "check u1 cant rewind" + runlocal git reset --hard HEAD^ + runlocal git push origin +master + expect "remote: + refs/heads/master aa u1 DENIED by fallthru" + expect "remote: error: hook declined to update refs/heads/master" + expect "\[remote rejected\] master -> master (hook declined)" + + cd ~/td + rm -rf aa + name "check tester can rewind" + runlocal git clone gitolite:aa + expect "Cloning into aa..." + cd aa + runlocal git reset --hard HEAD^ + runlocal git push origin +master + expect_push_ok "master -> master (forced update)" + + cd ~/td + rm -rf aa + name "check u4 can only read" + runlocal git clone u4:aa + expect "Cloning into aa..." + cd aa + mdc; mdc; mdc + runlocal git push origin master + expect "W access for aa DENIED to u4" + + cd ~/td + rm -rf aa + name "check u6 cant even read" + runlocal git clone u6:aa + expect "Cloning into aa..." + expect "R access for aa DENIED to u6" + + # now the same thing with the repo-on-disk missing + runremote rm -rf repositories/aa.git + + cd ~/td + name "repo on disk missing: u1" + runlocal git clone u1:aa + expect "fatal: 'repositories/aa.git' does not appear to be a git repository" + + name "repo on disk missing: tester" + runlocal git clone gitolite:aa + expect "fatal: 'repositories/aa.git' does not appear to be a git repository" + + name "repo on disk missing: u4" + runlocal git clone u4:aa + expect "fatal: 'repositories/aa.git' does not appear to be a git repository" + + name "repo on disk missing: u6" + runlocal git clone u6:aa + expect "R access for aa DENIED to u6" + + # now the same thing with a repo that is not even in the config + + cd ~/td + name "repo on disk missing: u1" + runlocal git clone u1:bb + expect "R access for bb DENIED to u1" + + name "repo on disk missing: tester" + runlocal git clone gitolite:bb + expect "R access for bb DENIED to tester" + + name "repo on disk missing: u4" + runlocal git clone u4:bb + expect "R access for bb DENIED to u4" + + name "repo on disk missing: u6" + runlocal git clone u6:bb + expect "R access for bb DENIED to u6" + + name INTERNAL +done + From a12eb15252b964296f9a36de7e42a1031218e181 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 31 Jul 2010 00:46:26 +0530 Subject: [PATCH 415/850] make compile atomic by writing a different and rename-ing when done --- src/gl-compile-conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index d9577c8..b1277fc 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -382,7 +382,7 @@ for my $fragment_file (glob("conf/fragments/*.conf")) parse_conf_file($fragment_file, $fragment); } -my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED ); +my $compiled_fh = wrap_open( ">", "$GL_CONF_COMPILED.new" ); my $data_version = $current_data_version; print $compiled_fh Data::Dumper->Dump([$data_version], [qw(*data_version)]); my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); @@ -393,6 +393,7 @@ $dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g print $compiled_fh $dumped_data; print $compiled_fh Data::Dumper->Dump([\%groups], [qw(*groups)]) if $GL_BIG_CONFIG and %groups; close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; +rename "$GL_CONF_COMPILED.new", "$GL_CONF_COMPILED"; # ---------------------------------------------------------------------------- # (that ends the config file compiler and write) From f0c280cd389a32d67eb230e5cc9039e883b24ea5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 30 Jul 2010 21:24:39 +0530 Subject: [PATCH 416/850] allow "info" to have some chance of working on big-config setups! Fedora, till now, had no hope in hell of running the info command. Why? Because the output of the info command is semantically the same as the output of the compile script *before* the big-config mode was created. And we all know how _that_ went ;-) So now you get to give "info" a partial reponame or a pattern, just like in the case of "expand". And if you're under GL_BIG_CONFIG this pattern is mandatory. And if you try to cheat it'll still stop after showing 5 entries to prevent (accidental?) DOSs Anyway, see doc changes in this commit for more details. --- doc/3-faq-tips-etc.mkd | 31 ++++++++++++++++++++++++++----- doc/report-output.mkd | 15 ++++++++------- src/gitolite.pm | 30 ++++++++++++++++++++++++------ 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 345ac77..25a83ff 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -455,15 +455,36 @@ Easy! Just use ssh to give the "info" command to the gitolite server: @R @W testing #R W vkc -To understand what these symbols mean, please see doc/report-output.mkd. The -administrator can also say things like: +To understand what these symbols mean, please see doc/report-output.mkd. + +You can also pass an extra argument to subset the listing; it's treated as a +regex pattern that can match anywhere in the reponame. + +In "big-config" mode (i.e., when `GL_BIG_CONFIG` is set) this argument is +**mandatory**. You can try and cheat the system by passing in a "." but +gitolite truncates the output after 5 results to prevent a DOS. + + $ ssh git@server info git + hello sitaram, the gitolite version here is v1.4.2-4-g40cbecd + the gitolite config gives you the following access: + @R W git-notes + @R W gitolite + #R W gitolite-admin + + $ ssh git@server info admin + hello sitaram, the gitolite version here is v1.4.2-4-g40cbecd + the gitolite config gives you the following access: + #R W gitolite-admin + +The administrator can also say things like: # if you installed using the "from-client" method - ssh gitolite info u1 u2 u3 + ssh gitolite info foo u1 u2 u3 # for the other 3 install methods - ssh git@server info u1 u2 u3 + ssh git@server info foo u1 u2 u3 -to get this info for other user(s). +to get this info for other user(s). The "foo" is a partial reponame or a +regex pattern; see above for details. diff --git a/doc/report-output.mkd b/doc/report-output.mkd index 51cb565..ba632e4 100644 --- a/doc/report-output.mkd +++ b/doc/report-output.mkd @@ -13,14 +13,15 @@ Here is the output of the 2 commands (info and expand): Usage: - ssh git@server info - ssh git@server info [list of users] + ssh git@server info [optional_pattern [list of users]] The "info" command shows you all the repos (and repo patterns) in the config -file that you have been given any kind of access to. If you're an admin you -can append a list of users to see their permissions instead of your own. -(Side note: if you installed using easy-install that would probably be `ssh -gitolite info`, by the way). +file that you have been given any kind of access to. If you supply an +optional pattern the output will be limited to repos matching that pattern. +If you're an admin you can append a list of users to see their permissions +instead of your own; in this mode the pattern is mandatory, even if you just +use `.` to cheat. (Side note: if you installed using easy-install that would +probably be `ssh gitolite info`, by the way). The meaning of C, R, and W are self-explanatory, but they might sometimes be prefixed by a symbol. For example, `@R` means that `@all` users have @@ -31,7 +32,7 @@ root's shell prompt) and so has access to `@all` repos. Usage: - ssh git@server expand [optional pattern] + ssh git@server expand [optional_pattern] The "expand" command trawls through all the repositories on the server, limiting to repos matching the pattern you provide (default is all repos diff --git a/src/gitolite.pm b/src/gitolite.pm index dbcfcfc..8eee76c 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -325,7 +325,7 @@ sub report_version { # got parsed by the compile script sub report_basic { - my($GL_ADMINDIR, $GL_CONF_COMPILED, $user) = @_; + my($GL_ADMINDIR, $GL_CONF_COMPILED, $repo, $user) = @_; # XXX The correct way is actually to give parse_acl another argument # (defaulting to $ENV{GL_USER}, the value being used now). But for now @@ -338,7 +338,11 @@ sub report_basic # send back some useful info if no command was given &report_version($GL_ADMINDIR, $user); print "\rthe gitolite config gives you the following access:\r\n"; + my $count = 0; for my $r (sort keys %repos) { + next unless $r =~ /$repo/i; + # if $GL_BIG_CONFIG is on, limit the number of output lines to 5 + next if $GL_BIG_CONFIG and $count++ >= 5; if ($r =~ $REPONAME_PATT and $r !~ /\bCREAT[EO]R\b/) { &parse_acl($GL_CONF_COMPILED, $r, "NOBODY", "NOBODY", "NOBODY"); } else { @@ -354,6 +358,7 @@ sub report_basic $perm .= ( $repos{$r}{W}{'@all'} ? ' @W' : ( $repos{'@all'}{W}{$user} ? ' #W' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ))); print "$perm\t$r\r\n" if $perm =~ /\S/; } + print "only 5 out of $count candidate repos examined\r\nplease use a partial reponame or regex pattern to limit output\r\n" if $GL_BIG_CONFIG and $count > 5; } # ---------------------------------------------------------------------------- @@ -374,17 +379,20 @@ sub expand_wild # has at least "R" access to chdir("$repo_base_abs") or die "chdir $repo_base_abs failed: $!\n"; + my $count = 0; for my $actual_repo (`find . -type d -name "*.git"|sort`) { chomp ($actual_repo); $actual_repo =~ s/^\.\///; $actual_repo =~ s/\.git$//; # actual_repo has to match the pattern being expanded - next unless $actual_repo =~ /$repo/; + next unless $actual_repo =~ /$repo/i; + next if $GL_BIG_CONFIG and $count++ >= 5; my($perm, $creator, $wild) = &repo_rights($actual_repo); next unless $perm =~ /\S/; print "$perm\t$creator\t$actual_repo\n"; } + print "only 5 out of $count candidate repos examined\nplease use a partial reponame or regex pattern to limit output\n" if $GL_BIG_CONFIG and $count > 5; } # there will be multiple calls to repo_rights; better to use a closure. We @@ -471,18 +479,28 @@ sub special_cmd # check each special command we know about and call it if enabled if ($cmd eq 'info') { - &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user); + &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, '^', $user); print "you also have shell access\r\n" if $shell_allowed; } elsif ($cmd =~ /^info\s+(.+)$/) { my @otherusers = split ' ', $1; + # the first argument is assumed to be a repo pattern, like in the + # expand command + my $repo = shift(@otherusers); + die "$repo has invalid characters" unless "x$repo" =~ $REPOPATT_PATT; + print STDERR "(treating $repo as pattern to limit output)\n"; - my($perm, $creator, $wild) = &repo_rights('gitolite-admin'); - die "you can't ask for others' permissions\n" unless $perm =~ /W/; + # set up the list of users being queried; it's either a list passed in + # (allowed only for admin pushers) or just $user + if (@otherusers) { + my($perm, $creator, $wild) = &repo_rights('gitolite-admin'); + die "you can't ask for others' permissions\n" unless $perm =~ /W/; + } + push @otherusers, $user unless @otherusers; &parse_acl($GL_CONF_COMPILED); for my $otheruser (@otherusers) { warn("ignoring illegal username $otheruser\n"), next unless $otheruser =~ $USERNAME_PATT; - &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $otheruser); + &report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $repo, $otheruser); } } elsif ($HTPASSWD_FILE and $cmd eq 'htpasswd') { &ext_cmd_htpasswd($HTPASSWD_FILE); From 7056a1a653b99495446bf0e093abe17321f3ce6d Mon Sep 17 00:00:00 2001 From: Adilson Carvalho Date: Tue, 3 Aug 2010 23:22:29 -0300 Subject: [PATCH 417/850] Edited a little English mistake --- doc/big-config.mkd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/big-config.mkd b/doc/big-config.mkd index c70b866..bbe126d 100644 --- a/doc/big-config.mkd +++ b/doc/big-config.mkd @@ -210,7 +210,7 @@ some way (not using `~/.ssh/authorized_keys`) of invoking gitolite. Such setups also have "usergroups", and a way to tell, for each user, what groups he/she is a member of. So let's say "alice" works on projects "foo" -and "bar", while "bob" is works on project "bar" and is a member of the +and "bar", while "bob" works on project "bar" and is a member of the `L3_support` team. You can use those group names in the gitolite config file for access control From 5753557b521692c5e0ccaea049966764a17aece5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 9 Aug 2010 22:48:59 +0530 Subject: [PATCH 418/850] mob branches document! (wow, that easy? hmm...) --- doc/mob-branches.mkd | 76 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 doc/mob-branches.mkd diff --git a/doc/mob-branches.mkd b/doc/mob-branches.mkd new file mode 100644 index 0000000..757fff6 --- /dev/null +++ b/doc/mob-branches.mkd @@ -0,0 +1,76 @@ +# mob branches in gitolite + +WARNING: This is hairy stuff. But what's life without a little danger? + +WARNING 2: girocco does mob branches quite differently; the controls on what a +mob branch can do are much more fundamental. Here we just trick gitolite into +accepting anonymous ssh connections and pretending they're from a mythical +user called "mob". **This means all the access control is -- as you might +expect -- in the gitolite.conf file, so make sure you don't give the `mob` +user too many rights!** + +(tested on Fedora 13; assumes your gitolite server userid is "gitolite" and +install was "from-client" method; adjust according to your environment. If +you need more than this, you should not be enabling mob branches anyway ;-) + +[hah! Easy way out of being badgered with questions!] + + * create a file called `/tmp/mobshell` (put it somewhere more permanent if + you wish). This file should be `chmod +x` and contain + + #!/bin/sh + shift + export SSH_ORIGINAL_COMMAND + SSH_ORIGINAL_COMMAND="$*" + + /home/gitolite/.gitolite/src/gl-auth-command mob + # see one of the lines in ~gitolite/.ssh/authorized_keys for the + # precise location of the gl-auth-command script + + * create a user called mob. Give it the same UID number and `$HOME` as your + gitolite server userid, and set the login shell to be the script you just + created. Also delete the password. + + id -u gitolite + # returns 503 or something... + useradd -d /home/gitolite -s /tmp/mobshell -u 503 -o mob + passwd -d mob + + * make sure you have a recent enough sshd and put these lines at the bottom, + then restart sshd + + Match user mob + PermitEmptyPasswords yes + +That's it. Now you can add stuff to your gitolite.conf file. Here's some +examples: + + * This allows the mob user to do anything to the "mob" branch: + + repo foo + RW+ = alice bob + R = eve + RW+ mob$ = mob + # only the mob branch, nothing more + + * This is the same, except it can be any branch under "mob/" so you get some + flexibility: + + RW+ mob/ = mob + + * Girocco allows pushing to the mob branch only if it already exists (that + is, the mob user cannot *create* the mob branch, but if it already exists + he can push to it). Here's how you'd do that in gitolite: + + repo foo + RW+C = alice bob + R = eve + RW+ mob$ = mob + + * This gives *every* repo a mob branch (be careful!) + + repo @all + RW+ mob$ = mob + +How do mob users access it? The URLs just look like: `mob@server:repo` +instead of `gitolite@server:repo` That's it! From c1eeaf3c2c3155bf9b6b76e8690856516c870269 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 23 Jun 2010 20:00:23 +0530 Subject: [PATCH 419/850] detect gl-emergency-addkey attempt for server side installs and give a suitable message, fix the doc, etc. (error found by "ry" on #git backlog; couldn't contact him later) --- doc/6-ssh-troubleshooting.mkd | 3 +-- src/gl-emergency-addkey | 43 +++++++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 636907c..ca7e0b8 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -429,8 +429,7 @@ first place, so the simplest way is to enable it from the server side only. If you lost the admin key, and need to re-establish ownership of the gitolite-admin repository with a fresh key, take a look at the `src/gl-emergency-addkey` program. You will need shell access to the server -of course. The top of the script has useful information on how to use it and -what it needs. +of course. Run it without arguments to get instructions. diff --git a/src/gl-emergency-addkey b/src/gl-emergency-addkey index f10027b..4a95499 100755 --- a/src/gl-emergency-addkey +++ b/src/gl-emergency-addkey @@ -1,30 +1,49 @@ #!/bin/sh -# what/why: re-establish gitolite admin access when admin key(s) lost -# where: on server (NOT client!) +# Use this to re-establish gitolite admin access when admin key(s) are lost. -# pre-req: shell access to the server (even with password is fine) -# pre-work: - make yourself a new keypair on your workstation -# - copy the pubkey and this script to the server +# RUN THIS ON THE SERVER, NOT THE CLIENT! (so you need shell access to the +# server; even with password is fine) -# usage: $0 admin_name client_host_shortname pubkeyfile -# notes: - admin_name should already have RW or RW+ access to the -# gitolite-admin repo -# - client_host_shortname is any simple word; see example below +# HOW TO USE (substitute your admin name for "sitaram" below) +# - make yourself a new keypair on your workstation +# - copy the pubkey to the server, call it "sitaram.pub" and put it in $HOME +# - run this command: +# ~/.gitolite/src/gl-emergency-addkey sitaram emergency sitaram.pub + +# this will add a new key called sitaram@emergency.pub. Since the "userid" +# that key pertains to is "sitaram", whoever has the private key for this now +# has the same rights as "sitaram" # WARNING: ABSOLUTELY NO ARGUMENT CHECKING DONE # WARNING: NEWER GITS ONLY ON SERVER SIDE (for now) -# example: $0 sitaram laptop /tmp/sitaram.pub -# result: a new keyfile named sitaram@laptop.pub would be added - # ENDHELP +# we can't use this program unless it was installed using gl-easy-install +GL_PACKAGE_CONF=$( cd; perl -e 'do ".gitolite.rc"; print $GL_PACKAGE_CONF' ) +if [ -n "$GL_PACKAGE_CONF" ] +then + cat < Date: Thu, 24 Jun 2010 12:03:58 +0200 Subject: [PATCH 420/850] Better warning message for multi-keys per pubkey file "WARNING: a pubkey file can only have one line (key); ignoring $pubkey" message was a bit confusing, because elsewhere the docs claim multiple keys are suported. Added note on how to add multiple keys for single user and pointer to the doc file concerned. --- src/gl-compile-conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index b1277fc..f80c035 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -607,7 +607,10 @@ for my $pubkey (`find . -type f`) # don't trust files with multiple lines (i.e., something after a newline) if ($pubkey_content =~ /\n./) { - print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n"; + print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n" . + " If you want to add multiple public keys for a single user, use\n" . + " \"user\@host.pub\" file names. See the \"one user, many keys\"\n" . + " section in doc/3-faq-tips-etc.mkd for details.\n"; next; } print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; From 416d3398b34778a9ace7d202b41dd4bfc352c716 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 25 Jun 2010 20:09:38 +0530 Subject: [PATCH 421/850] (minor) update example RC file docs on GL_GITCONFIG_KEYS usage --- conf/example.gitolite.rc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 272d618..5114349 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -132,6 +132,12 @@ $GL_GITCONFIG_KEYS = ""; # regular expressions) is to allow anything and everything: # $GL_GITCONFIG_KEYS = ".*"; +# NOTE that due to some quoting and interpolation issues I have not been able +# to look at, a literal "." needs to be specified in this string as \\. (two +# backslashes and a dot). So this is how you'd allow any keys in the "foo" +# category: +# $GL_GITCONFIG_KEYS = "foo\\..*"; + # -------------------------------------- # EXTERNAL COMMAND HELPER -- HTPASSWD From 97126608b937588a4ea6d5ac3018adae0f605c53 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 25 Jun 2010 20:10:55 +0530 Subject: [PATCH 422/850] (minor) fixes to test driver --- t/test-driver.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/test-driver.sh b/t/test-driver.sh index 416000d..8473d5b 100755 --- a/t/test-driver.sh +++ b/t/test-driver.sh @@ -30,13 +30,13 @@ hl() { # highlight function echo $normal fi } -pause() { echo pausing\; hit enter or ctrl-c...; read; } +pause() { echo pausing, "$@"\; hit enter or ctrl-c...; read; } capture() { cf=$1; shift; "$@" >& $TESTDIR/$cf; } editrc() { scp gitolite-test@localhost:.gitolite.rc ~/junk >/dev/null - perl -pi -e "print STDERR if /$1/ and s/=.*/= $2;/" ~/junk + perl -pi -e "print STDERR if not /^#/ and /$1/ and s/=.*/= $2;/" ~/junk scp ~/junk gitolite-test@localhost:.gitolite.rc >/dev/null } From fbb9dafbd188e8f41ddb73845fab68f0f9e6a36e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 20 Jul 2010 12:39:06 +0530 Subject: [PATCH 423/850] gqt -- gitolite quick (re-)test sometimes I want to quickly test a few lines of change within the context of a currently-running/just-ran test, *without* doing the rollback etc. Here's how you do that now: - in your source tree, make the change and then run: cp -a src hooks contrib/adc /some/tmp/place - go to the tester userid and re-run your tests like so: GQT=/some/tmp/place ./test-driver.sh it'll rollback as normal then overwrite src and hooks from $GQT Also, there's now a "dbg" sub that can be used for quick printf-style debugging. --- src/gitolite.pm | 6 ++++++ t/rollback | 2 +- t/rollback.server | 9 +++++++++ t/test-driver.sh | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 8eee76c..0ce43bf 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -57,6 +57,12 @@ sub wrap_open { return $fh; } +sub dbg { + for my $i (@_) { + print STDERR "DBG: $i\n"; + } +} + sub log_it { my ($ip, $logmsg); open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n"; diff --git a/t/rollback b/t/rollback index 84f3872..43894e3 100755 --- a/t/rollback +++ b/t/rollback @@ -8,4 +8,4 @@ tar xf rollback.tar mkdir td scp -q $TESTDIR/rollback.server gitolite-test@localhost:rollback || { echo scp failed; exit 1; } -ssh gitolite-test@localhost ./rollback +ssh gitolite-test@localhost ./rollback $GQT diff --git a/t/rollback.server b/t/rollback.server index b9c7e45..f8b84ec 100755 --- a/t/rollback.server +++ b/t/rollback.server @@ -3,3 +3,12 @@ cd rm -rf .ssh .gitolite .gitolite.rc repositories gitolite-install tar xf rollback.tar + +# if arg1 is a directory, we have to overwrite sources from there (see commit +# message for details) +if [ -d "$1" ] +then + cd .gitolite + /bin/cp -a $1/{src,hooks} . + cd +fi diff --git a/t/test-driver.sh b/t/test-driver.sh index 8473d5b..3461f9c 100755 --- a/t/test-driver.sh +++ b/t/test-driver.sh @@ -54,6 +54,7 @@ ugc () git push ${1:-gitolite}:gitolite-admin master git fetch origin >/dev/null 2>&1 ) >~/1 2>~/2 + grep DBG: ~/2 >/dev/null && grep . ~/1 ~/2 } mdc() From 4fa5442daa04ee999b5188465ddb97be5e504ae8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Jul 2010 03:23:59 +0530 Subject: [PATCH 424/850] two months too late for towel day... but "dont-panic"! gl-emergency-addkey replaced by totally new gl-dont-panic, which does more (including recovering from a botched push, not just lost keys), is cleaner, and works for all install methods --- doc/6-ssh-troubleshooting.mkd | 4 +- src/gl-dont-panic | 96 +++++++++++++++++++++++++++++++++++ src/gl-emergency-addkey | 59 --------------------- src/gl-install | 2 +- 4 files changed, 99 insertions(+), 62 deletions(-) create mode 100755 src/gl-dont-panic delete mode 100755 src/gl-emergency-addkey diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index ca7e0b8..d129aff 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -428,8 +428,8 @@ first place, so the simplest way is to enable it from the server side only. If you lost the admin key, and need to re-establish ownership of the gitolite-admin repository with a fresh key, take a look at the -`src/gl-emergency-addkey` program. You will need shell access to the server -of course. Run it without arguments to get instructions. +`src/gl-dont-panic` program. You will need shell access to the server of +course. Run it without arguments to get instructions. diff --git a/src/gl-dont-panic b/src/gl-dont-panic new file mode 100755 index 0000000..5bdd398 --- /dev/null +++ b/src/gl-dont-panic @@ -0,0 +1,96 @@ +#!/bin/sh + +usage() { + cat < Date: Sun, 25 Jul 2010 09:01:04 +0530 Subject: [PATCH 425/850] (minor) gl-setup fixes - stop erroring out if run from elsewhere than $HOME (by localising the "cd" we need somewhere in between) - catch the admin@home.pub usage early - minor fix to the backticked commands - gl-setup now does 'chmod go-rwx .ssh' --- src/gl-setup | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/gl-setup b/src/gl-setup index b12d9fd..d19b6b2 100755 --- a/src/gl-setup +++ b/src/gl-setup @@ -28,6 +28,7 @@ then echo $pubkey_file | grep '.pub$' >/dev/null || die "$pubkey_file must end in .pub" [ -f $pubkey_file ] || die "cant find $pubkey_file" admin_name=` basename $pubkey_file .pub` + echo $admin_name | grep '@' >/dev/null && die "please don't use '@' in the initial admin name" fi if [ -f $HOME/.gitolite.rc ] @@ -54,17 +55,20 @@ fi # setup ssh stuff. We break our normal rule that we will not fiddle with # authkeys etc., because in this case it seems appropriate -cd -mkdir -p .ssh -touch .ssh/authorized_keys -chmod go-w . .ssh .ssh/authorized_keys +( + cd $HOME + mkdir -p .ssh + chmod go-rwx .ssh + touch .ssh/authorized_keys + chmod go-w . .ssh .ssh/authorized_keys +) # now we get to gitolite itself gl-install -q -GL_ADMINDIR=` cd;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'` -REPO_BASE=` cd;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ` +GL_ADMINDIR=` cd $HOME;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'` +REPO_BASE=` cd $HOME;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ` [ -f $GL_ADMINDIR/conf/gitolite.conf ] || { cat < $GL_ADMINDIR/conf/gitolite.conf From 2dc02e9a75a16c2446155275afe7cc9ffdb536c7 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Jul 2010 09:31:24 +0530 Subject: [PATCH 426/850] (minor) document GL_BYPASS_UPDATE_HOOK --- doc/3-faq-tips-etc.mkd | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 25a83ff..4b0a473 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -27,6 +27,7 @@ In this document: * support for git installed outside default PATH * "personal" branches * custom hooks and custom git config + * bypassing gitolite * INconvenience features * deleting a repo * helping with gitweb @@ -558,6 +559,29 @@ You can specify hooks that you want to propagate to all repos, as well as per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and `conf/example.conf` for details. + + +##### bypassing gitolite + +Sometimes you'll need to access one of the gitolite-managed repos directly on +the server, without going through gitolite. Reasons may be some automatic +updates or some other ad hoc purposes you can dream up. + +Cloning a gitolite-controlled repo is easy enough -- just use the full path +(typically `~/repositories/reponame.git`) instead of just `reponame`, to +compensate for gitolite not sitting in between and adding those things to the +repo path. + +But when you push, the update hook (which git will invoke anyway) will fail +because it needs all sorts of access control info that it now doesn't have, +because the push was invoked without going through gitolite. + +In order to bypass the update hook, just set the `GL_BYPASS_UPDATE_HOOK` +environment variable to "1" or something, export it, and push. I prefer not +to set that variable permanently, preferring this mode instead: + + GL_BYPASS_UPDATE_HOOK=1 git push + #### INconvenience features From c8879264e69f859b9143f53a5db39fb79a2c8d9b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 25 Jul 2010 20:55:32 +0530 Subject: [PATCH 427/850] separate out the code that sets up ~/.ssh/authorized_keys NOTE: there are no *functional* changes in this for *normal* gitolite users. It's just a chunk of code moving into a new subroutine etc. KDE needs to populate the authkeys file from an LDAP store. Other large projects may have similar means to store keys, depending on how they do their user provisioning so a generic solution is worth exploring. This means that in these special cases - the gitolite-admin repo's keydir/ directory is not needed [1] - but they still need to create the authkeys file somehow Implementation: - write a shim program to make the authkeys-generation code callable from the command line/shell. - set $GL_NO_SETUP_AUTHKEYS=1 in the rc file to disable authkey generation during a "compile" (admin repo push) Expected usage of new program gl-setup-authkeys: - LDAP change triggers some script - this script collects all keys from LDAP, puts them in some directory, and then calls gl-setup-authkeys, passing it the name of the directory ALSO PLEASE SEE COMMENTS AT THE TOP OF THE NEW PROGRAM IN THIS COMMIT FOR SOME IMPORTANT DISCUSSION. ---- Footnotes: [1] It doesn't make sense to use it if the keys will be maintained by some other entity and can be called up as needed, and it adds an unnecessary extra step. --- src/gitolite.pm | 111 ++++++++++++++++++++++++++++++++++++++++++ src/gl-compile-conf | 97 ++---------------------------------- src/gl-setup-authkeys | 54 ++++++++++++++++++++ 3 files changed, 168 insertions(+), 94 deletions(-) create mode 100755 src/gl-setup-authkeys diff --git a/src/gitolite.pm b/src/gitolite.pm index 0ce43bf..dbb8152 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -472,6 +472,117 @@ sub cli_repo_rights { print "$perm $creator\n"; } +# ---------------------------------------------------------------------------- +# setup the ~/.ssh/authorized_keys file +# ---------------------------------------------------------------------------- + +sub setup_authkeys +{ + # ARGUMENTS + + my($bindir, $GL_KEYDIR, $user_list_p) = @_; + # calling from outside the normal compile script may mean that argument 2 + # may not be passed; so make sure it's a valid hashref, even if empty + $user_list_p = {} unless $user_list_p; + + # CONSTANTS + + # command and options for authorized_keys + my $AUTH_COMMAND="$bindir/gl-auth-command"; + my $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; + + # START + + my $authkeys_fh = wrap_open( "<", $ENV{HOME} . "/.ssh/authorized_keys", + "\tFor security reasons, gitolite will not *create* this file if it does\n" . + "\tnot already exist. Please see the \"admin\" document for details\n"); + my $newkeys_fh = wrap_open( ">", $ENV{HOME} . "/.ssh/new_authkeys" ); + # save existing authkeys minus the GL-added stuff + while (<$authkeys_fh>) + { + print $newkeys_fh $_ unless (/^# gito(sis-)?lite start/../^# gito(sis-)?lite end/); + } + + # add our "start" line, each key on its own line (prefixed by command and + # options, in the standard ssh authorized_keys format), then the "end" line. + print $newkeys_fh "# gitolite start\n"; + wrap_chdir($GL_KEYDIR); + my @not_in_config; # pubkeys exist but users don't appear in the config file + for my $pubkey (`find . -type f`) + { + chomp($pubkey); $pubkey =~ s(^\./)(); + + # security check (thanks to divVerent for catching this) + unless ($pubkey =~ $REPONAME_PATT) { + print STDERR "$pubkey contains some unsavoury characters; ignored...\n"; + next; + } + + # lint check 1 + unless ($pubkey =~ /\.pub$/) + { + print STDERR "WARNING: pubkey files should end with \".pub\", ignoring $pubkey\n"; + next; + } + + my $user = $pubkey; + $user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub + $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz + + # lint check 2 -- don't print right now; just collect the messages + push @not_in_config, "$user($pubkey)" if %$user_list_p and not $user_list_p->{$user}; + $user_list_p->{$user} = 'has pubkey' if %$user_list_p; + # apparently some pubkeys don't end in a newline... + my $pubkey_content; + { + local $/ = undef; + local @ARGV = ($pubkey); + $pubkey_content = <>; + } + $pubkey_content =~ s/\s*$/\n/; + # don't trust files with multiple lines (i.e., something after a newline) + if ($pubkey_content =~ /\n./) + { + print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n" . + " If you want to add multiple public keys for a single user, use\n" . + " \"user\@host.pub\" file names. See the \"one user, many keys\"\n" . + " section in doc/3-faq-tips-etc.mkd for details.\n"; + next; + } + print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; + print $newkeys_fh $pubkey_content; + } + + # lint check 2 -- print less noisily + if (@not_in_config > 10) { + print STDERR "$WARN You have " . scalar(@not_in_config) . " pubkeys that do not appear to be used in the config\n"; + } elsif (@not_in_config) { + print STDERR "$WARN the following users (pubkey files in parens) do not appear in the config file:\n", join(",", sort @not_in_config), "\n"; + } + + # lint check 3; a little more severe than the first two I guess... + { + my @no_pubkey = + grep { $_ !~ /^(gitweb|daemon|\@.*|~\$creator|\$readers|\$writers)$/ } + grep { $user_list_p->{$_} ne 'has pubkey' } + keys %{$user_list_p}; + if (@no_pubkey > 10) { + print STDERR "$WARN You have " . scalar(@no_pubkey) . " users WITHOUT pubkeys...!\n"; + } elsif (@no_pubkey) { + print STDERR "$WARN the following users have no pubkeys:\n", join(",", sort @no_pubkey), "\n"; + } + } + + print $newkeys_fh "# gitolite end\n"; + close $newkeys_fh or die "$ABRT close newkeys failed: $!\n"; + + # all done; overwrite the file (use cat to avoid perm changes) + system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); + system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys") + and die "couldn't write authkeys file\n"; + system("rm $ENV{HOME}/.ssh/new_authkeys"); +} + # ---------------------------------------------------------------------------- # S P E C I A L C O M M A N D S # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index f80c035..d4b7289 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -54,7 +54,7 @@ open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB, $GL_NO_CREATE_REPOS, $GL_NO_SETUP_AUTHKEYS); # and these are set by gitolite.pm -our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); +our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $ABRT, $WARN); # the common setup module is in the same directory as this running program is my $bindir = $0; @@ -73,10 +73,6 @@ $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; # definitions specific to this program # ---------------------------------------------------------------------------- -# command and options for authorized_keys -$AUTH_COMMAND="$bindir/gl-auth-command"; -$AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; - # groups can now represent user groups or repo groups. # $groups{group}{member} = "master" (or name of fragment file in which the @@ -559,93 +555,6 @@ unless ($GL_NO_DAEMON_NO_GITWEB) { # "compile" ssh authorized_keys # ---------------------------------------------------------------------------- -# NOTE: for now we assume that setting up authkeys is the LAST thing we do! -exit 0 if $GL_NO_SETUP_AUTHKEYS; - -my $authkeys_fh = wrap_open( "<", $ENV{HOME} . "/.ssh/authorized_keys", - "\tFor security reasons, gitolite will not *create* this file if it does\n" . - "\tnot already exist. Please see the \"admin\" document for details\n"); -my $newkeys_fh = wrap_open( ">", $ENV{HOME} . "/.ssh/new_authkeys" ); -# save existing authkeys minus the GL-added stuff -while (<$authkeys_fh>) -{ - print $newkeys_fh $_ unless (/^# gito(sis-)?lite start/../^# gito(sis-)?lite end/); +unless ($GL_NO_SETUP_AUTHKEYS) { + &setup_authkeys($bindir, $GL_KEYDIR, \%user_list); } - -# add our "start" line, each key on its own line (prefixed by command and -# options, in the standard ssh authorized_keys format), then the "end" line. -print $newkeys_fh "# gitolite start\n"; -wrap_chdir($GL_KEYDIR); -my @not_in_config; # pubkeys exist but users don't appear in the config file -for my $pubkey (`find . -type f`) -{ - chomp($pubkey); $pubkey =~ s(^\./)(); - - # security check (thanks to divVerent for catching this) - unless ($pubkey =~ $REPONAME_PATT) { - print STDERR "$pubkey contains some unsavoury characters; ignored...\n"; - next; - } - - # lint check 1 - unless ($pubkey =~ /\.pub$/) - { - print STDERR "WARNING: pubkey files should end with \".pub\", ignoring $pubkey\n"; - next; - } - - my $user = $pubkey; - $user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub - $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz - - # lint check 2 -- don't print right now; just collect the messages - push @not_in_config, "$user($pubkey)" unless $user_list{$user}; - $user_list{$user} = 'has pubkey'; - # apparently some pubkeys don't end in a newline... - my $pubkey_content = `cat $pubkey`; - $pubkey_content =~ s/\s*$/\n/; - # don't trust files with multiple lines (i.e., something after a newline) - if ($pubkey_content =~ /\n./) - { - print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n" . - " If you want to add multiple public keys for a single user, use\n" . - " \"user\@host.pub\" file names. See the \"one user, many keys\"\n" . - " section in doc/3-faq-tips-etc.mkd for details.\n"; - next; - } - print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; - print $newkeys_fh $pubkey_content; -} - -# lint check 2 -- print less noisily -if (@not_in_config > 10) { - print STDERR "$WARN You have " . scalar(@not_in_config) . " pubkeys that do not appear to be used in the config\n"; -} elsif (@not_in_config) { - print STDERR "$WARN the following users (pubkey files in parens) do not appear in the config file:\n", join(",", sort @not_in_config), "\n"; -} - -# lint check 3; a little more severe than the first two I guess... -{ - my @no_pubkey = - grep { $_ !~ /^(gitweb|daemon|\@.*|~\$creator|\$readers|\$writers)$/ } - grep { $user_list{$_} ne 'has pubkey' } - keys %user_list; - if (@no_pubkey > 10) { - print STDERR "$WARN You have " . scalar(@no_pubkey) . " users WITHOUT pubkeys...!\n"; - } elsif (@no_pubkey) { - print STDERR "$WARN the following users have no pubkeys:\n", join(",", sort @no_pubkey), "\n"; - } -} - -print $newkeys_fh "# gitolite end\n"; -close $newkeys_fh or die "$ABRT close newkeys failed: $!\n"; - -# all done; overwrite the file (use cat to avoid perm changes) -system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); -system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys") - and die "couldn't write authkeys file\n"; -system("rm $ENV{HOME}/.ssh/new_authkeys"); - -# NOTE: if you're adding code here that is unrelated to setting up authkeys, -# remember that control may not reach here if a sysadm has set -# GL_NO_SETUP_AUTHKEYS in the rc file. diff --git a/src/gl-setup-authkeys b/src/gl-setup-authkeys new file mode 100755 index 0000000..377f798 --- /dev/null +++ b/src/gl-setup-authkeys @@ -0,0 +1,54 @@ +#!/usr/bin/perl -w + +# shim program + +# arg-1: keydir + +# - an external program populates "keydir" with *all* keys and then +# calls us, giving "keydir" as arg-1 +# - we then call gitolite.pm's "setup_authkeys" function to do its thing + +# IMPLEMENTATION NOTE: make sure this is in the same directory as +# "gitolite.pm" and all the rest of "src/". + +# DISCUSSION: +# +# For now, we will assume *all* the keys are in the keydir passed. The +# setup_authkeys routine factored out from the old gl-compile-conf is +# not setup to take a partial set of keys and create the +# ~/.ssh/authorized_keys file. +# +# Also, there are issues to do with *deleted* keys that need to be taken +# care of. +# +# All in all, unless it is shown to be quite inefficient, I'd much +# prefer processing *all* keys each time there is a change. + +# setup +my $bindir = $0; +$bindir =~ s/\/[^\/]+$//; +$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//; +require "$bindir/gitolite.pm"; + +# prevent newbie from running it accidentally and clobbering his authkeys +# file! +if (@ARGV and $ARGV[0] eq '-batch') { + shift; +} else { + print STDERR " + This is a cronnable, batchable, program to rewrite ~/.ssh/authorized_keys + using public keys in a given directory. + + If you are ABSOLUTELY sure you know what you're doing, here's how: + + $0 -batch keydir + + where 'keydir' contains a bunch of '*.pub' files\n\n"; + exit 1; +} + +# quick sanity check and run +my $keydir = shift or die "I need a directory name\n"; +-d $keydir or die "$keydir should be a directory\n"; + +&setup_authkeys($bindir, $keydir); From 74e15d06a0e031c468c5e77183678e094f95f9cf Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 26 Jun 2010 05:30:15 +0530 Subject: [PATCH 428/850] add "addrc" function to test driver --- t/test-driver.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/test-driver.sh b/t/test-driver.sh index 3461f9c..d634b39 100755 --- a/t/test-driver.sh +++ b/t/test-driver.sh @@ -40,6 +40,13 @@ editrc() { scp ~/junk gitolite-test@localhost:.gitolite.rc >/dev/null } +addrc() { + ssh gitolite-test@localhost cat .gitolite.rc < /dev/null > ~/junk + tee -a ~/junk + echo '1;' >> ~/junk + scp ~/junk gitolite-test@localhost:.gitolite.rc >/dev/null +} + ugc () { ( From fda10c280579d0c49cafd1aba594581859f1dfa5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 10 Aug 2010 13:42:52 +0530 Subject: [PATCH 429/850] mirroring support... conf/example.gitolite.rc - "slave mode" flag to disable pushes and "list of slaves" hooks/common/post-receive.mirrorpush - code to push to the mirror, creating the repo if needed src/mirror-shell - shell for master pushing to a slave, because we don't actually want to go through gitolite itself, yet we have to take care of $REPO_BASE being wherever. And of course we have to set GL_BYPASS_UPDATE_HOOK to 1 for the push to happen! src/gl-mirror-sync - manually runnable program to sync from current server to another --- conf/example.gitolite.rc | 7 + doc/mirroring.mkd | 313 +++++++++++++++++++++++++++ hooks/common/post-receive.mirrorpush | 21 ++ src/gl-auth-command | 9 +- src/gl-mirror-shell | 20 ++ src/gl-mirror-sync | 38 ++++ 6 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 doc/mirroring.mkd create mode 100755 hooks/common/post-receive.mirrorpush create mode 100755 src/gl-mirror-shell create mode 100755 src/gl-mirror-sync diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 5114349..88cd66c 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -13,6 +13,13 @@ # $GL_PACKAGE_HOOKS = ""; # -------------------------------------- +# MIRRORING SUPPORT + +# $GL_SLAVE_MODE = 0; +# $ENV{GL_SLAVES} = 'gitolite@server2 gitolite@server3'; +# PLEASE USE SINGLE QUOTES ABOVE, NOT DOUBLE QUOTES + +# see doc/mirroring.mkd for details # -------------------------------------- diff --git a/doc/mirroring.mkd b/doc/mirroring.mkd new file mode 100644 index 0000000..40988b0 --- /dev/null +++ b/doc/mirroring.mkd @@ -0,0 +1,313 @@ +# mirroring a gitolite setup + +Mirroring git repos is essentially a one-liner. For each mirror you want to +update, you just add a post-receive hook that says + + #!/bin/bash + git push --mirror slave_user@mirror.host:/path/to/repo.git + +But life is never that simple... + +**This document has been tested using a 3-server setup, all installed using +the "non-root" method (see doc/0-INSTALL.mkd). However, the process is +probably not going to be very forgiving of human error -- like anything that +is this deep in "system admin" territory, errors are likely to be costly. If +you're the kind who hits enter first and then thinks about what he typed, +you're in for some fun times ;-)** + +**On the plus side, everything we do is done using git commands, so things are +never *really* lost until you do a `git gc`**. + +---- + +In this document: + + * RULE NUMBER ONE! + * things that will NOT be mirrored by this process + * conventions in this document + * setting up mirroring + * install gitolite on all servers + * generate keypairs + * setup the mirror-shell on each server + * set slaves to slave mode + * set slave server lists + * syncing the mirrors the first time + * switching over + * the return of foo + * switching back + * making foo a slave + * URLs that your users will use + + + +### RULE NUMBER ONE! + +**RULE OF GIT MIRRORING: users should push directly to only one server**! All +the other machines (the slaves) should be updated by the master server. + +If a user pushes directly to one of the slaves, those changes will get wiped +out on the next mirror push from the real master server. + +Corollary: if the primary went down and you effected a changeover, you must +make sure that the primary does not come up in a push-enabled mode when it +recovers. + + + +### things that will NOT be mirrored by this process + +Let's get this out of the way. This procedure will only mirror your git +repositories, using `git push --mirror`. Therefore, certain files will not be +mirrored: + + * gitolite log files + * "gl-creator" and "gl-perms" files + * "projects.list", "description", and entries in the "config" files within + each repo + +None of these affect actual repo contents of course, but they could be +important, (especially the gl-creator, although if your wildcard pattern had +"CREATOR" in it you can recreate those files easily enough anyway). + +Your best bet is to use rsync for the log files, and tar for the others, at +regular intervals. + + + +### conventions in this document + +The userid hosting gitolite is `gitolite` on all machines. The servers are +foo, bar, and baz. At the beginning, foo is the master, the other 2 are +slaves. + + + +### setting up mirroring + + + +#### install gitolite on all servers + + * before running the final step in the install sequence, make sure you go to + the `hooks/common` directory and rename `post-receive.mirrorpush` to + `post-receive`. See doc/hook-propagation.mkd if you're not sure where you + should look for `hooks/common`. + + * if the server already has gitolite installed, use the normal methods to + make sure this hook gets in. + + * Use the same "admin key" on all the machines, so that the same person has + gitolite-admin access to all of them. + + + +#### generate keypairs + +Each server will be potentially logging on to one or more of the other +servers, so first generate keypairs for all of them (`ssh-keygen`) and copy +the `.pub` files to all other servers, named appropriately. So foo will have +bar.pub and baz.pub, etc. + + + +#### setup the mirror-shell on each server + +If you installed gitolite using the from client method, run the following: + + # on foo + export GL_ADMINDIR=` cd $HOME;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'` + cat bar.pub baz.pub | + sed -e 's,^,command="'$GL_ADMINDIR'/src/gl-mirror-shell" ,' >> ~/.ssh/authorized_keys + +If you installed using any of the other 3 methods do this: + + cat bar.pub baz.pub | + sed -e 's,^,command="'$(which gl-mirror-shell)'" ,' >> ~/.ssh/authorized_keys + +Also do the same thing on the other machines. + +Now test this access: + + # on foo + ssh gitolite@bar pwd + # should print /home/gitolite/repositories + ssh gitolite@bar uname -a + # should print the appropriate info for that server + +Similarly test the other combinations. + + + +#### set slaves to slave mode + +Set slave mode on all the *slave* servers by setting `$GL_SLAVE_MODE = 1` +(uncommenting the line if necessary). + +Leave the master server's file as is. + + + +#### set slave server lists + +On the master (foo), set the names of the slaves by editing the +`~/.gitolite.rc` to contain: + + $ENV{GL_SLAVES} = 'gitolite@bar gitolite@baz'; + +**Note the syntax well; this is critical**: + + * **this must be in single quotes** (or you must remember to escape the `@`) + * the variable is an ENV var, not a plain perl var + * the values are *space separated* + * each value represents the userid and hostname for one server + +The basic idea is that this string, should be usable in both the following +syntaxes: + + git clone gitolite@bar:repo + ssh gitolite@bar pwd + +You can also use ssh host aliases. Let's say server "bar" has a non-standard +port number: + + # in ~/.ssh/config on foo + host mybar + hostname bar + user gitolite + port 2222 + + # in ~/.gitolite.rc on foo + $ENV{GL_SLAVES} = 'bar gitolite@baz'; + +And that's really all there is, unless... + + + +### syncing the mirrors the first time + +This is fine if you're setting up everything from scratch. But if your master +server already had some repos with commits on them, you have to manually sync +them up once. + + # on foo + gl-mirror-sync gitolite@bar + # path to "sync" program is ~/.gitolite/src if "from-client" install + + + +### switching over + +Let's say foo goes down. You want to make bar the main server, and continue +to have "baz" be a slave. + + * on bar, edit `~/.gitolite.rc` and set + + $GL_SLAVE_MODE = 0; + $ENV{GL_SLAVES} = 'gitolite@baz'; + + * **sanity check**: go to your gitolite-admin clone, add a remote for "bar", + fetch it, and make sure they are the same: + + git remote add bar gitolite@bar:gitolite-admin + git fetch bar + git branch -a -v + # check that all SHAs are the same + + * inform everyone of the new URL for their repos (see next section for more + on this) + + * make sure that if "foo" does come up, it will not immediately start + serving requests. You'll be in trouble if (a) foo comes up as it was + before, and (b) some developer still had the old URL lying around and + started pushing changes to it. + + You could jump in quickly and set `$GL_SLAVE_MODE = 1` as soon as the + system comes up. Better still, use extraneous means to block incoming + connections from normal users (out of scope for this document). + + + +### the return of foo + + + +#### switching back + +Switching back is fairly easy. + + * synchronise all repos from bar to foo. This may take some time, depending + on how long foo was down. + + # on bar + gl-mirror-sync gitolite@foo + # path to "sync" program is ~/.gitolite/src if "from-client" install + + * turn off pushes on "bar" by setting slave mode to 1 + * run the sync once again; this should complete quickly + + * **double check by comparing some the repos on both sides if needed**. You + could run the following snippet on all servers for a quick check: + + cd ~/repositories # or wherever $REPO_BASE is + find . -type d -name "*.git" | sort | + while read r + do + echo $r + git ls-remote $r | sort + done | md5sum + + * on foo, set the slave list (or check that it is correct) + * on foo, set slave mode off + * tell everyone to switch back + + + +#### making foo a slave + +If "foo" does come up in a controlled manner, you might not want to switch +back right away. Unless you're doing DNS tricks, users may be peeved at +having to do 2 switches. + +If you want to make foo a slave, you know the drill by now: + + * set slave mode to 1 on foo + * on bar, add foo as a slave + + # in ~/.gitolite.rc on bar + $ENV{GL_SLAVES} = 'gitolite@foo gitolite@baz'; + +I think that should cover pretty much everything. I *have* tested most of +this, but YMMV. + +---- + + + +### URLs that your users will use + +Unless you play DNS tricks, it is more than likely that your users would have +to change the URLs they use to access their repos if you change the server +they push to. + +I cannot speak for the plethora of git client software out there but for +normal git, this problem can be mitigated somewhat by doing this: + + * in `~/.ssh/config` on my workstation, I have + + host gl + hostname=primary.server.ip + user=gitolite + + * all my `git clone` commands use `gl:reponame` as the URL + + * if the primary goes down, and I have to access the secondary, I just + change the `hostname` line in `~/.ssh/config`. + +That's it. Every clone of every repo used anywhere in this userid is now +changed. + +To repeat, this may or may not work with all the git clients that exist (like +jgit, or any of the GUI tools, and especially if you're on Windows). + +If anyone has a better idea, something that works more universally, I'd love +to hear it. diff --git a/hooks/common/post-receive.mirrorpush b/hooks/common/post-receive.mirrorpush new file mode 100755 index 0000000..5188236 --- /dev/null +++ b/hooks/common/post-receive.mirrorpush @@ -0,0 +1,21 @@ +#!/bin/bash + +# gitolite mirroring + +# please see doc/mirroring.mkd for instructions on how to use this + +if [ -n "$GL_SLAVES" ] +then + for mirror in $GL_SLAVES + do + if git push --mirror $mirror:$GL_REPO.git + then + : + else + ssh $mirror mkdir -p $GL_REPO.git + ssh $mirror git init --bare $GL_REPO.git + git push --mirror $mirror:$GL_REPO.git || + echo "WARNING: mirror push to $mirror failed" + fi + done +fi >&2 diff --git a/src/gl-auth-command b/src/gl-auth-command index e9ab1b0..fdce93f 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -23,7 +23,7 @@ use warnings; # ---------------------------------------------------------------------------- # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $GL_SLAVE_MODE); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; @@ -112,6 +112,13 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) { $ENV{SSH_ORIGINAL_COMMAND} = 'info'; } +# ---------------------------------------------------------------------------- +# slave mode should not do much +# ---------------------------------------------------------------------------- + +die "server is in slave mode; you can only fetch\n" + if ($GL_SLAVE_MODE and $ENV{SSH_ORIGINAL_COMMAND} !~ /^(info|expand|get|git-upload-)/); + # ---------------------------------------------------------------------------- # admin defined commands # ---------------------------------------------------------------------------- diff --git a/src/gl-mirror-shell b/src/gl-mirror-shell new file mode 100755 index 0000000..6689aef --- /dev/null +++ b/src/gl-mirror-shell @@ -0,0 +1,20 @@ +#!/bin/bash + +export GL_BYPASS_UPDATE_HOOK +GL_BYPASS_UPDATE_HOOK=1 + +export REPO_BASE=`cd $HOME;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ` +if echo $SSH_ORIGINAL_COMMAND | egrep git-upload\|git-receive >/dev/null +then + + # the (special) admin post-update hook needs these, so we cheat + export GL_ADMINDIR + export GL_BINDIR + GL_ADMINDIR=` cd $HOME;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'` + GL_BINDIR=`echo $0 | perl -lpe 's/^/$ENV{PWD}\// unless /^\//; s/\/[^\/]+$//;'` + + SSH_ORIGINAL_COMMAND=`echo $SSH_ORIGINAL_COMMAND | sed -e "s/'/'$REPO_BASE\//"` + exec git shell -c "$SSH_ORIGINAL_COMMAND" +else + bash -c "cd $REPO_BASE; $SSH_ORIGINAL_COMMAND" +fi diff --git a/src/gl-mirror-sync b/src/gl-mirror-sync new file mode 100755 index 0000000..9b6d195 --- /dev/null +++ b/src/gl-mirror-sync @@ -0,0 +1,38 @@ +#!/bin/bash + +mirror=$1 +[ -z "$1" ] && { echo need \"user@host\" or ssh hostalias; exit 1; } +ssh -o PasswordAuthentication=no $mirror echo hello-there | grep hello-there >/dev/null || + { echo I cant ssh to $mirror; exit 1; } + +cd $HOME +REPO_BASE=` cd $HOME;perl -e 'do ".gitolite.rc"; print $REPO_BASE'` +cd $REPO_BASE + +ssh $mirror cat \$HOME/.gitolite.rc | expand | egrep '^ *\$GL_SLAVE_MODE *= *1; *$' >/dev/null || { + echo $mirror does not seem to be in slave mode + exit 1; +} + +find . -type d -name "*.git" | cut -c3- | sort | while read r +do + cd $HOME; cd $REPO_BASE; cd $r + printf "$r " + + if [ `git rev-parse HEAD` = "HEAD" ] + then + echo is empty\; skipping + continue + fi + + # this is essentially the same code as in the post-receive hook + if git push --mirror $mirror:$r + then + : + else + ssh $mirror mkdir -p $r + ssh $mirror git init --bare $r + git push --mirror $mirror:$r || + echo "WARNING: mirror push to $mirror failed" + fi < /dev/null +done From c944a8a3b91c08af6fbe3963f0d169972b93e293 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 25 Jun 2010 21:11:35 +0530 Subject: [PATCH 430/850] new test: t54-repo-configs --- t/t54-repo-configs | 87 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 t/t54-repo-configs diff --git a/t/t54-repo-configs b/t/t54-repo-configs new file mode 100644 index 0000000..4e53921 --- /dev/null +++ b/t/t54-repo-configs @@ -0,0 +1,87 @@ +# vim: syn=sh: +for wr in 0 1 +do + for bc in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS $wr + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "fail to set foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar + RW+ = @leads + RW = @devs + config foo.bar = baz + " | ugc + expect "remote: git config foo.bar not allowed" + runremote cat repositories/bar.git/config + notexpect '^.foo' + notexpect 'bar = baz' + + name "update rc file to allow foo.*" + catrc + cp ~/1 ~/junk + perl -pi -e 's/GL_GITCONFIG_KEYS = ""/GL_GITCONFIG_KEYS = "foo\\\\..*"/' ~/junk + cat ~/junk | runremote dd of=.gitolite.rc + catrc + expect "GL_GITCONFIG_KEYS.*foo" + + name "ok to set foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar + RW+ = @leads + RW = @devs + config foo.bar = baz + " | ugc -r + expect "remote: Initialized empty Git repository in /home/gitolite-test/repositories/bar.git/" + notexpect "git config.*not allowed" + expect_push_ok "master -> master" + runremote cat repositories/bar.git/config + expect '^.foo' + expect 'bar = baz' + + name "fail to set foobar.baz" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar + RW+ = @leads + RW = @devs + config foo.bar = baz + config foobar.baz = ooka + " | ugc -r + expect "remote: git config foobar.baz not allowed" + runremote cat repositories/bar.git/config + expect '^.foo' + expect 'bar = baz' + notexpect '^.foobar' + notexpect 'baz = ooka' + + name "delete foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar + RW+ = @leads + RW = @devs + config foo.bar = + " | ugc -r + runremote cat repositories/bar.git/config + expect '^.foo' + notexpect 'bar = baz' + + name INTERNAL + done +done From d1d2c3e054ba0ec592a9560dd0185122363a249c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 25 Jun 2010 23:36:15 +0530 Subject: [PATCH 431/850] git config settings in wild repos: part 1 - new GL_GITCONFIG_WILD to gate it - new sub to do all the hard work (refactored from a few lines in compile) - split the call from "compile" into two sets -- first for non-wild, then for wild This ensures that after a "compile" (admin push) all git configs are applied. TODO: apply them when a new wild repo is created by a user, and then on the "fork" (admin-defined command) --- conf/example.gitolite.rc | 8 ++++++++ src/gitolite.pm | 44 ++++++++++++++++++++++++++++++++++++++++ src/gl-compile-conf | 26 ++++++++++++++++-------- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 88cd66c..263266d 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -145,6 +145,14 @@ $GL_GITCONFIG_KEYS = ""; # category: # $GL_GITCONFIG_KEYS = "foo\\..*"; +# -------------------------------------- +# ALLOW GITCONFIG KEYS EVEN FOR WILD REPOS +# +# This is an efficiency issue more than a security issue, since this requires +# trawling through all of $REPO_BASE looking for stuff :) + +# $GL_GITCONFIG_WILD = 0; + # -------------------------------------- # EXTERNAL COMMAND HELPER -- HTPASSWD diff --git a/src/gitolite.pm b/src/gitolite.pm index dbb8152..8700b9e 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -243,6 +243,50 @@ sub get_set_desc } } +# ---------------------------------------------------------------------------- +# set/unset repo configs +# ---------------------------------------------------------------------------- + +sub setup_repo_configs +{ + my ($repo_config_p, $repo, $wild) = @_; + + wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git"); + + # prep for wild and non-wild cases separately + my $repo_patt = ''; # actually "repo or repo_pattern" + if ($wild) { + chomp (my $creator = `cat gl-creater`); + + # loop each key of %repo_config and make a copy + for my $key (keys %$repo_config_p) { + my $key2 = $key; + # subst $creator in the copy with the creator name + $key2 =~ s/\$creator/$creator/g; + # match the new key against $repo + if ($repo =~ /^$key2$/) { + # if it matches, proceed + $repo_patt = $key; + last; + } + } + print STDERR "$WARN repo $repo created by $creator is wild but doesn't match any patterns\n" unless $repo_patt; + } else { + $repo_patt ||= $repo; # just use the repo itself... + # XXX TODO there is a remote possibility of errors if you have a + # normal repo that fits a wild pattern; needs some digging into... + } + + while ( my ($key, $value) = each(%{ $repo_config_p->{$repo_patt} }) ) { + if ($value) { + $value =~ s/^"(.*)"$/$1/; + system("git", "config", $key, $value); + } else { + system("git", "config", "--unset-all", $key); + } + } +} + # ---------------------------------------------------------------------------- # parse the compiled acl # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index d4b7289..eedff31 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB, $GL_NO_CREATE_REPOS, $GL_NO_SETUP_AUTHKEYS); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_GITCONFIG_WILD, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB, $GL_NO_CREATE_REPOS, $GL_NO_SETUP_AUTHKEYS); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $ABRT, $WARN); @@ -311,6 +311,7 @@ sub parse_conf_file for my $repo (@repos) # each repo in the current stanza { $repo_config{$repo}{$key} = $value; + print STDERR "$WARN git config set for $repo but \$GL_GITCONFIG_WILD not set\n" unless $repo =~ $REPONAME_PATT or $GL_GITCONFIG_WILD; } } # include @@ -382,6 +383,7 @@ my $compiled_fh = wrap_open( ">", "$GL_CONF_COMPILED.new" ); my $data_version = $current_data_version; print $compiled_fh Data::Dumper->Dump([$data_version], [qw(*data_version)]); my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]); +$dumped_data .= Data::Dumper->Dump([\%repo_config], [qw(*repo_config)]) if %repo_config; # the dump uses single quotes, but we convert any strings containing $creator, # $readers, $writers, to double quoted strings. A wee bit sneaky, but not too # much... @@ -476,14 +478,20 @@ unless ($GL_NO_CREATE_REPOS) { # config hooks.emailprefix = "[foo]" for my $repo (keys %repo_config) { - wrap_chdir("$repo_base_abs/$repo.git"); - while ( my ($key, $value) = each(%{ $repo_config{$repo} }) ) { - if ($value) { - $value =~ s/^"(.*)"$/$1/; - system("git", "config", $key, $value); - } else { - system("git", "config", "--unset-all", $key); - } + &setup_repo_configs(\%repo_config, $repo) if $repo =~ $REPONAME_PATT; +} + +# wild +if (%repo_config and $GL_WILDREPOS and $GL_GITCONFIG_WILD) { + wrap_chdir("$repo_base_abs"); + for my $repo (`find . -type d -name "*.git"`) { + chomp ($repo); + next unless -f "$ENV{GL_REPO_BASE_ABS}/$repo/gl-creater"; # all/only wild repos have this file + + # $repo will look like "./foo/bar.git", make it "foo/bar" before + # calling the repo config setup sub + $repo =~ s(\./(.*)\.git$)($1); + &setup_repo_configs(\%repo_config, $repo, 1); } } From ad64f9952203e9827aeca53bee5c2cf1c9241536 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 26 Jun 2010 05:21:57 +0530 Subject: [PATCH 432/850] git config settings in wild repos: part 2 call it after the new_repo in auth also --- src/gl-auth-command | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gl-auth-command b/src/gl-auth-command index fdce93f..623ab91 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -28,6 +28,7 @@ our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMIND our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; our %groups; +our %repo_config; # the common setup module is in the same directory as this running program is my $bindir = $0; @@ -206,6 +207,7 @@ if ($perm =~ /C/) { # it was missing, and you have create perms wrap_chdir("$repo_base_abs"); new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); + &setup_repo_configs(\%repo_config, $repo, 1); wrap_chdir($ENV{HOME}); } From 3172c67dbeccca7c7ba7461f4389d23d1f27f382 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 26 Jun 2010 06:30:31 +0530 Subject: [PATCH 433/850] new tests: t55 and t56 for repo config with wildrepos --- t/t55-repo-configs-wild-without-CREATOR | 126 ++++++++++++++++++++++++ t/t56-repo-configs-wild-with-CREATOR | 126 ++++++++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 t/t55-repo-configs-wild-without-CREATOR create mode 100644 t/t56-repo-configs-wild-with-CREATOR diff --git a/t/t55-repo-configs-wild-without-CREATOR b/t/t55-repo-configs-wild-without-CREATOR new file mode 100644 index 0000000..68303cf --- /dev/null +++ b/t/t55-repo-configs-wild-without-CREATOR @@ -0,0 +1,126 @@ +# vim: syn=sh: +for gcw in 0 1 +do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + + name INTERNAL + editrc GL_WILDREPOS 1 + echo "\$GL_GITCONFIG_WILD = $gcw;" | addrc + + # ---------- + + name "fail to set foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = baz + " | ugc + expect "remote: git config foo.bar not allowed" + + name "update rc file to allow foo.*" + catrc + cp ~/1 ~/junk + perl -pi -e 's/GL_GITCONFIG_KEYS = ""/GL_GITCONFIG_KEYS = "foo\\\\..*"/' ~/junk + cat ~/junk | runremote dd of=.gitolite.rc + catrc + expect "GL_GITCONFIG_KEYS.*foo" + + name "ok to set foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = baz + " | ugc -r + [ "$gcw" = "0" ] && expect "remote: git config set for bar/..\* but \$GL_GITCONFIG_WILD not set" + [ "$gcw" = "1" ] && notexpect "remote: git config set for bar/..\* but \$GL_GITCONFIG_WILD not set" + notexpect "git config.*not allowed" + expect_push_ok "master -> master" + + [ "$gcw" = "0" ] && continue # the rest of these tests don't make sense now + + name "fail to set foobar.baz" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = baz + config foobar.baz = ooka + " | ugc -r + expect "remote: git config foobar.baz not allowed" + + name "u1 create bar/try1" + runlocal git ls-remote u1:bar/try1 + expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/try1.git/" + + name "check u1 has foo.bar" + runremote cat repositories/bar/try1.git/config + expect '^.foo' + expect 'bar = baz' + + name "delete foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = + " | ugc -r + expect_push_ok "master -> master" + + name "check u1 doesnt have foo.bar" + runremote cat repositories/bar/try1.git/config + expect '^.foo' # git leaves the section header behind + notexpect 'bar = baz' + + name "u2 create bar/try2" + runlocal git ls-remote u2:bar/try2 + expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/try2.git/" + + name "check u2 doesnt have foo.bar" + runremote cat repositories/bar/try2.git/config + notexpect '^.foo' # but not here, since this repo never had the key at all + notexpect 'bar = baz' + + name "add foo.frob retroactively" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.frob = nitz + " | ugc -r + expect_push_ok "master -> master" + + name "check u1 has foo.frob" + runremote cat repositories/bar/try1.git/config + expect '^.foo' + expect 'frob = nitz' + + name "check u2 has foo.frob" + runremote cat repositories/bar/try2.git/config + expect '^.foo' + expect 'frob = nitz' + + name INTERNAL +done diff --git a/t/t56-repo-configs-wild-with-CREATOR b/t/t56-repo-configs-wild-with-CREATOR new file mode 100644 index 0000000..71bee9c --- /dev/null +++ b/t/t56-repo-configs-wild-with-CREATOR @@ -0,0 +1,126 @@ +# vim: syn=sh: +for gcw in 0 1 +do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + + name INTERNAL + editrc GL_WILDREPOS 1 + echo "\$GL_GITCONFIG_WILD = $gcw;" | addrc + + # ---------- + + name "fail to set foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/CREATOR/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = baz + " | ugc + expect "remote: git config foo.bar not allowed" + + name "update rc file to allow foo.*" + catrc + cp ~/1 ~/junk + perl -pi -e 's/GL_GITCONFIG_KEYS = ""/GL_GITCONFIG_KEYS = "foo\\\\..*"/' ~/junk + cat ~/junk | runremote dd of=.gitolite.rc + catrc + expect "GL_GITCONFIG_KEYS.*foo" + + name "ok to set foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/CREATOR/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = baz + " | ugc -r + [ "$gcw" = "0" ] && expect "remote: git config set for bar/\$creator/..\* but \$GL_GITCONFIG_WILD not set" + [ "$gcw" = "1" ] && notexpect "remote: git config set for bar/\$creator/..\* but \$GL_GITCONFIG_WILD not set" + notexpect "git config.*not allowed" + expect_push_ok "master -> master" + + [ "$gcw" = "0" ] && continue # the rest of these tests don't make sense now + + name "fail to set foobar.baz" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/CREATOR/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = baz + config foobar.baz = ooka + " | ugc -r + expect "remote: git config foobar.baz not allowed" + + name "u1 create bar/u1/try1" + runlocal git ls-remote u1:bar/u1/try1 + expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/u1/try1.git/" + + name "check u1 has foo.bar" + runremote cat repositories/bar/u1/try1.git/config + expect '^.foo' + expect 'bar = baz' + + name "delete foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/CREATOR/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = + " | ugc -r + expect_push_ok "master -> master" + + name "check u1 doesnt have foo.bar" + runremote cat repositories/bar/u1/try1.git/config + expect '^.foo' # git leaves the section header behind + notexpect 'bar = baz' + + name "u2 create bar/u2/try2" + runlocal git ls-remote u2:bar/u2/try2 + expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/u2/try2.git/" + + name "check u2 doesnt have foo.bar" + runremote cat repositories/bar/u2/try2.git/config + notexpect '^.foo' # but not here, since this repo never had the key at all + notexpect 'bar = baz' + + name "add foo.frob retroactively" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo bar/CREATOR/..* + C = @leads + RW+ = CREATOR + RW = @leads + config foo.frob = nitz + " | ugc -r + expect_push_ok "master -> master" + + name "check u1 has foo.frob" + runremote cat repositories/bar/u1/try1.git/config + expect '^.foo' + expect 'frob = nitz' + + name "check u2 has foo.frob" + runremote cat repositories/bar/u2/try2.git/config + expect '^.foo' + expect 'frob = nitz' + + name INTERNAL +done From 33d052dc7ddd8c7061f85b8ff869e4a19a3a7de0 Mon Sep 17 00:00:00 2001 From: "Kevin P. Fleming" Date: Mon, 28 Jun 2010 15:16:55 -0500 Subject: [PATCH 434/850] it is not an error for a wildcard repository to not match any patterns... that just means it does not have any config keys defined --- src/gitolite.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 8700b9e..0a36bff 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -270,7 +270,6 @@ sub setup_repo_configs last; } } - print STDERR "$WARN repo $repo created by $creator is wild but doesn't match any patterns\n" unless $repo_patt; } else { $repo_patt ||= $repo; # just use the repo itself... # XXX TODO there is a remote possibility of errors if you have a From 61802045d9d3fb9240a7b8bb77cca866802d70dc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 29 Jun 2010 10:55:36 +0530 Subject: [PATCH 435/850] (mini refactor) clean up the repo_base_abs stuff make it a sort of "super global" (an ENV var) all through, because *everyone* seems to need it *and* this variable is pretty much constant for the entire install --- src/gitolite.pm | 28 ++++++++++++++-------------- src/gl-auth-command | 10 +++++----- src/gl-compile-conf | 10 +++++----- src/gl-install | 6 +++--- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 0a36bff..be59488 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -182,17 +182,17 @@ sub new_repo # "who created this repo", "am I on the R list", and "am I on the RW list"? sub wild_repo_rights { - my ($repo_base_abs, $repo, $user) = @_; + my ($repo, $user) = @_; # creator my $c = ''; - if ( -f "$repo_base_abs/$repo.git/gl-creater") { - my $fh = wrap_open("<", "$repo_base_abs/$repo.git/gl-creater"); + if ( -f "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-creater") { + my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-creater"); chomp($c = <$fh>); } # $user's R and W rights my ($r, $w); $r = ''; $w = ''; - if ($user and -f "$repo_base_abs/$repo.git/gl-perms") { - my $fh = wrap_open("<", "$repo_base_abs/$repo.git/gl-perms"); + if ($user and -f "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms") { + my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms"); my $perms = join ("", <$fh>); if ($perms) { $r = $user if $perms =~ /^\s*R(?=\s).*\s(\@all|$user)(\s|$)/m; @@ -209,10 +209,10 @@ sub wild_repo_rights sub get_set_perms { - my($repo_base_abs, $repo, $verb, $user) = @_; - my ($creator, $dummy, $dummy2) = &wild_repo_rights($repo_base_abs, $repo, ""); + my($repo, $verb, $user) = @_; + my ($creator, $dummy, $dummy2) = &wild_repo_rights($repo, ""); die "$repo doesnt exist or is not yours\n" unless $user eq $creator; - wrap_chdir("$repo_base_abs"); + wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); wrap_chdir("$repo.git"); if ($verb eq 'getperms') { system("cat", "gl-perms") if -f "gl-perms"; @@ -229,10 +229,10 @@ sub get_set_perms sub get_set_desc { - my($repo_base_abs, $repo, $verb, $user) = @_; - my ($creator, $dummy, $dummy2) = &wild_repo_rights($repo_base_abs, $repo, ""); + my($repo, $verb, $user) = @_; + my ($creator, $dummy, $dummy2) = &wild_repo_rights($repo, ""); die "$repo doesnt exist or is not yours\n" unless $user eq $creator; - wrap_chdir("$repo_base_abs"); + wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); wrap_chdir("$repo.git"); if ($verb eq 'getdesc') { system("cat", "description") if -f "description"; @@ -416,7 +416,7 @@ sub report_basic sub expand_wild { - my($GL_ADMINDIR, $GL_CONF_COMPILED, $repo_base_abs, $repo, $user) = @_; + my($GL_ADMINDIR, $GL_CONF_COMPILED, $repo, $user) = @_; &report_version($GL_ADMINDIR, $user); print "\ryou have access to the following repos on the server:\r\n"; @@ -427,7 +427,7 @@ sub expand_wild # display matching repos (from *all* the repos in the system) that $user # has at least "R" access to - chdir("$repo_base_abs") or die "chdir $repo_base_abs failed: $!\n"; + chdir("$ENV{GL_REPO_BASE_ABS}") or die "chdir $ENV{GL_REPO_BASE_ABS} failed: $!\n"; my $count = 0; for my $actual_repo (`find . -type d -name "*.git"|sort`) { chomp ($actual_repo); @@ -477,7 +477,7 @@ sub expand_wild if ($exists) { # these will be empty if it's not a wildcard repo anyway my ($read, $write); - ($creator, $read, $write) = &wild_repo_rights($ENV{GL_REPO_BASE_ABS}, $repo, $ENV{GL_USER}); + ($creator, $read, $write) = &wild_repo_rights($repo, $ENV{GL_USER}); # get access list with these substitutions $wild = &parse_acl($GL_CONF_COMPILED, $repo, $creator || "NOBODY", $read || "NOBODY", $write || "NOBODY"); } else { diff --git a/src/gl-auth-command b/src/gl-auth-command index 623ab91..aa4c5eb 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -53,7 +53,7 @@ $ENV{GL_WILDREPOS_DEFPERMS} = $GL_WILDREPOS_DEFPERMS if $GL_WILDREPOS_DEFPERMS; # set the umask before creating any files umask($REPO_UMASK); -my $repo_base_abs = $ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); # ---------------------------------------------------------------------------- # start... @@ -153,16 +153,16 @@ if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) { $verb eq 'expand' ? $repo = '^' : die "$verb needs an argument\n" unless $repo; if ($repo =~ $REPONAME_PATT and $verb =~ /getperms|setperms/) { # with an actual reponame, you can "getperms" or "setperms" - get_set_perms($repo_base_abs, $repo, $verb, $user); + get_set_perms($repo, $verb, $user); } elsif ($repo =~ $REPONAME_PATT and $verb =~ /(get|set)desc/) { # with an actual reponame, you can "getdesc" or "setdesc" - get_set_desc($repo_base_abs, $repo, $verb, $user); + get_set_desc($repo, $verb, $user); } elsif ($verb eq 'expand') { # with a wildcard, you can "expand" it to see what repos actually match die "$repo has invalid characters" unless "x$repo" =~ $REPOPATT_PATT; - expand_wild($GL_ADMINDIR, $GL_CONF_COMPILED, $repo_base_abs, $repo, $user); + expand_wild($GL_ADMINDIR, $GL_CONF_COMPILED, $repo, $user); } else { die "$cmd doesn't make sense to me\n"; } @@ -205,7 +205,7 @@ $ENV{GL_REPO}=$repo; my ($perm, $creator, $wild) = &repo_rights($repo); if ($perm =~ /C/) { # it was missing, and you have create perms - wrap_chdir("$repo_base_abs"); + wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); &setup_repo_configs(\%repo_config, $repo, 1); wrap_chdir($ENV{HOME}); diff --git a/src/gl-compile-conf b/src/gl-compile-conf index eedff31..25b4a67 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -433,10 +433,10 @@ die "\n\t\t***** AAARGH! *****\n" . # repo-base needs to be an absolute path for this loop to work right # so if it was not already absolute, prefix $HOME. -my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); unless ($GL_NO_CREATE_REPOS) { - wrap_chdir("$repo_base_abs"); + wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); # autocreate repos. Start with the ones that are normal repos in %repos my @repos = grep { $_ =~ $REPONAME_PATT and not /^@/ } sort keys %repos; @@ -453,7 +453,7 @@ unless ($GL_NO_CREATE_REPOS) { print STDERR "creating $repo...\n"; new_repo($repo, "$GL_ADMINDIR/hooks/common"); # new_repo would have chdir'd us away; come back - wrap_chdir("$repo_base_abs"); + wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); } # when repos are copied over from elsewhere, one had to run easy install @@ -483,7 +483,7 @@ for my $repo (keys %repo_config) { # wild if (%repo_config and $GL_WILDREPOS and $GL_GITCONFIG_WILD) { - wrap_chdir("$repo_base_abs"); + wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); for my $repo (`find . -type d -name "*.git"`) { chomp ($repo); next unless -f "$ENV{GL_REPO_BASE_ABS}/$repo/gl-creater"; # all/only wild repos have this file @@ -503,7 +503,7 @@ if (%repo_config and $GL_WILDREPOS and $GL_GITCONFIG_WILD) { # :-) These are now "pseduo users" -- giving them "R" access to a repo is all # you have to do -wrap_chdir("$repo_base_abs"); +wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); unless ($GL_NO_DAEMON_NO_GITWEB) { # daemons first... diff --git a/src/gl-install b/src/gl-install index 9454ad9..ad51af3 100755 --- a/src/gl-install +++ b/src/gl-install @@ -50,8 +50,8 @@ die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; # mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist -my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); -wrap_mkdir($repo_base_abs); +$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); +wrap_mkdir($ENV{GL_REPO_BASE_ABS}); wrap_mkdir($GL_ADMINDIR); # mkdir $GL_ADMINDIR's subdirs for my $dir qw(conf doc keydir logs src hooks hooks/common hooks/gitolite-admin) { @@ -80,7 +80,7 @@ EOF } # finally, hooks must be propagated to all the repos in case they changed -chdir("$repo_base_abs") or die "chdir $repo_base_abs failed: $!\n"; +chdir("$ENV{GL_REPO_BASE_ABS}") or die "chdir $ENV{GL_REPO_BASE_ABS} failed: $!\n"; for my $repo (`find . -type d -name "*.git"`) { chomp ($repo); # propagate our own, plus any local admin-defined, hooks From 509c73b8883474a74df3369bdb00af878f0ef43e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 16 Jul 2010 23:01:34 +0530 Subject: [PATCH 436/850] gitweb/daemon now work for wild repos also (thanks to Kevin Fleming for the need/use case) TODO: tests TODO: proper documentation; meanwhile, just read this: - you can give gitweb and daemon read rights to wild card repos also, and it'll all just work -- when a new repo is 'C'reated, it'll pick up those rights etc - you can assign descriptions (and owners) to individual repos as before, except now you can assign them to repos that actually were created from wild card patterns. So for example, you can define rules for repo foo/..* and then assign descriptions like foo/repo1 = "repo one" foo/repo2 = "repo two" foo/dil "scott" = "scott's dilbert repo" However, this only works for repos that already exist, and only when you push the admin repo. Thumb rule: have the user create his wild repo, *then* add and push the admin config file with the description. Not the other way around. implementation notes: - wildcard support for git config revamped, refactored... it's not just git config that needs wildcard support. daemon and gitweb access also will be needing it soon, so we start by factoring out the part that finds the "pattern" given a "real" repo name. - GL_NO_DAEMON_NO_GITWEB now gates more than just those two things; see doc/big-config.mkd for details - we trawl through $GL_REPO_BASE_ABS *once* only, collecting repo names and tying them to either the same name or to a wild pattern that the repo name was created from - nice little subs to setup gitweb, daemon, and git config - god bless $GL_REPOPATT and the day I decided to set that env var whenever a user hits a wild repo in any way :-) - the code in gl-compile-conf is very simple now. Much nicer than before --- src/gitolite.pm | 119 ++++++++++++++++++++++++++++-------- src/gl-auth-command | 7 ++- src/gl-compile-conf | 146 ++++++++++++++++++++------------------------ 3 files changed, 165 insertions(+), 107 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index be59488..bf70db6 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -115,6 +115,43 @@ sub ln_sf } } +# collect repo patterns for all %repos + +# for each repo passed (actual repos only please!), use either its own name if +# it exists as is in the repos hash, or find and use the pattern that matches + +sub collect_repo_patts +{ + my $repos_p = shift; + my %repo_patts = (); + + wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); + for my $repo (`find . -type d -name "*.git"`) { + chomp ($repo); + $repo =~ s(\./(.*)\.git$)($1); + # if its non-wild that's all you need + if ($repos_p->{$repo}) { + $repo_patts{$repo} = $repo; + } else { + # otherwise it gets a wee bit complicated ;-) + chomp (my $creator = `cat $repo.git/gl-creater`); + for my $key (keys %$repos_p) { + my $key2 = $key; + # subst $creator in the copy with the creator name + $key2 =~ s/\$creator/$creator/g; + # match the new key against $repo + if ($repo =~ /^$key2$/) { + # and if it matched you're done for this $repo + $repo_patts{$repo} = $key; + last; + } + } + } + } + + return %repo_patts; +} + # ---------------------------------------------------------------------------- # where is the rc file hiding? @@ -243,38 +280,17 @@ sub get_set_desc } } +# ---------------------------------------------------------------------------- +# IMPORTANT NOTE: next 3 subs (setup_*) assume $PWD is the bare repo itself +# ---------------------------------------------------------------------------- + # ---------------------------------------------------------------------------- # set/unset repo configs # ---------------------------------------------------------------------------- sub setup_repo_configs { - my ($repo_config_p, $repo, $wild) = @_; - - wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git"); - - # prep for wild and non-wild cases separately - my $repo_patt = ''; # actually "repo or repo_pattern" - if ($wild) { - chomp (my $creator = `cat gl-creater`); - - # loop each key of %repo_config and make a copy - for my $key (keys %$repo_config_p) { - my $key2 = $key; - # subst $creator in the copy with the creator name - $key2 =~ s/\$creator/$creator/g; - # match the new key against $repo - if ($repo =~ /^$key2$/) { - # if it matches, proceed - $repo_patt = $key; - last; - } - } - } else { - $repo_patt ||= $repo; # just use the repo itself... - # XXX TODO there is a remote possibility of errors if you have a - # normal repo that fits a wild pattern; needs some digging into... - } + my ($repo, $repo_patt, $repo_config_p) = @_; while ( my ($key, $value) = each(%{ $repo_config_p->{$repo_patt} }) ) { if ($value) { @@ -286,6 +302,57 @@ sub setup_repo_configs } } +# ---------------------------------------------------------------------------- +# set/unset daemon access +# ---------------------------------------------------------------------------- + +my $export_ok = "git-daemon-export-ok"; +sub setup_daemon_access +{ + my ($repo, $allowed) = @_; + + if ($allowed) { + system("touch $export_ok"); + } else { + unlink($export_ok); + } +} + +# ---------------------------------------------------------------------------- +# set/unset gitweb access +# ---------------------------------------------------------------------------- + +my $desc_file = "description"; +sub setup_gitweb_access +# this also sets "owner" for gitweb, by the way +{ + my ($repo, $allowed, $desc, $owner) = @_; + + if ($allowed) { + if ($desc) { + open(DESC, ">", $desc_file); + print DESC $desc . "\n"; + close DESC; + } + if ($owner) { + # set the repository owner + system("git", "config", "gitweb.owner", $owner); + } else { + # remove the repository owner setting + system("git config --unset-all gitweb.owner 2>/dev/null"); + } + } else { + unlink $desc_file; + system("git config --unset-all gitweb.owner 2>/dev/null"); + } + + # if there are no gitweb.* keys set, remove the section to keep the config file clean + my $keys = `git config --get-regexp '^gitweb\\.' 2>/dev/null`; + if (length($keys) == 0) { + system("git config --remove-section gitweb 2>/dev/null"); + } +} + # ---------------------------------------------------------------------------- # parse the compiled acl # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index aa4c5eb..bd7737a 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -23,7 +23,7 @@ use warnings; # ---------------------------------------------------------------------------- # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $GL_SLAVE_MODE); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $PROJECTS_LIST, $GL_SLAVE_MODE); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; @@ -207,7 +207,10 @@ if ($perm =~ /C/) { # it was missing, and you have create perms wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); - &setup_repo_configs(\%repo_config, $repo, 1); + &setup_repo_configs($repo, $ENV{GL_REPOPATT}, \%repo_config); + &setup_daemon_access($repo, $repos{$ENV{GL_REPOPATT}}{'R'}{'daemon'} || ''); + &setup_gitweb_access($repo, $repos{$ENV{GL_REPOPATT}}{'R'}{'gitweb'} || '', '', ''); + system("echo $repo.git >> $PROJECTS_LIST"); wrap_chdir($ENV{HOME}); } diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 25b4a67..8926a91 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -470,94 +470,82 @@ unless ($GL_NO_CREATE_REPOS) { } # ---------------------------------------------------------------------------- -# update repo configurations +# collect repo_patt for each actual repo # ---------------------------------------------------------------------------- -# no gating required for this. If you don't have any "config" lines it won't -# run anyway. An example of a config line could be: -# config hooks.emailprefix = "[foo]" +# go through each actual repo on disk, and match it to either its own name in +# the config (non-wild) or a wild pattern that matches it. Lots of things +# later will need this correspondence so we may as well snarf it in one shot -for my $repo (keys %repo_config) { - &setup_repo_configs(\%repo_config, $repo) if $repo =~ $REPONAME_PATT; -} -# wild -if (%repo_config and $GL_WILDREPOS and $GL_GITCONFIG_WILD) { - wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); - for my $repo (`find . -type d -name "*.git"`) { - chomp ($repo); - next unless -f "$ENV{GL_REPO_BASE_ABS}/$repo/gl-creater"; # all/only wild repos have this file +my %repo_patts = (); +%repo_patts = &collect_repo_patts(\%repos) unless $GL_NO_DAEMON_NO_GITWEB; - # $repo will look like "./foo/bar.git", make it "foo/bar" before - # calling the repo config setup sub - $repo =~ s(\./(.*)\.git$)($1); - &setup_repo_configs(\%repo_config, $repo, 1); +# NOTE: we're overloading GL_NO_DAEMON_NO_GITWEB to mean "no git config" also. +# In fact anything that requires trawling through the existing repos doing +# stuff to all of them is skipped if this variable is set. This is primarily +# for the Fedora folks, but it should be useful for anyone who has a huge set +# of repos and wants to manage gitweb/daemon/etc access via other means (they +# typically have the whole thing controlled by a web-app and a database +# anyway, and gitolite is only doing the access control and nothing more). + +# ---------------------------------------------------------------------------- +# various updates to all real repos +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# update repo configurations, gitweb description, daemon export-ok, etc +# ---------------------------------------------------------------------------- + +# all these require a "chdir" to the repo, so we club them for efficiency + +my %projlist = (); + +# for each real repo (and remember this will be empty, thus skipping all this, +# if $GL_NO_DAEMON_NO_GITWEB is on!) +for my $repo (keys %repo_patts) { + my $repo_patt = $repo_patts{$repo}; # if non-wild, $repo_patt will be eq $repo anyway + + wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git"); + + # git config + if ($repo_patt eq $repo or $GL_GITCONFIG_WILD) { + # erm, what that means is that it's either a non-wild repo being + # config'd or a wild one but gitconfig is allowed on wilds. + # XXX do we really need GL_GITCONFIG_WILD now? It was meant to be + # only an efficiency thing, but that was before this whole revamp; + # we already trawl through $REPO_BASE exactly once now anyway! + # ...need to think about this + &setup_repo_configs($repo, $repo_patt, \%repo_config) if $repo_config{$repo_patt}; + } + + # daemon is easy + &setup_daemon_access($repo, $repos{$repo_patt}{'R'}{'daemon'} || ''); + + # gitweb is a little more complicated. Here're some notes: + # - "setup_gitweb_access" also sets "owner", despite the name + # - specifying a description also counts as enabling gitweb + # - description and owner are not specified for wildrepos; they're + # specified for *actual* repos, even if the repo was created by a + # wild card spec and "C" permissions. If you see the + # conf/example.conf file, you will see that repo owner/desc don't go + # into the "repo foo" section; they're essentialy independent. + # Anyway, I believe it doesn't make sense to have all wild repos + # (for some pattern) to have the same description and owner. + if ($repos{$repo_patt}{'R'}{'gitweb'} or $desc{"$repo.git"}) { + $projlist{"$repo.git"} = 1; + &setup_gitweb_access($repo, 1, $desc{"$repo.git"} || '', $owner{"$repo.git"} || ''); + } else { + &setup_gitweb_access($repo, 0, '', ''); } } -# ---------------------------------------------------------------------------- -# handle gitweb and daemon -# ---------------------------------------------------------------------------- - -# I just assume you'll never have any *real* users called "gitweb" or "daemon" -# :-) These are now "pseduo users" -- giving them "R" access to a repo is all -# you have to do - -wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); - -unless ($GL_NO_DAEMON_NO_GITWEB) { - # daemons first... - for my $repo (sort keys %repos) { - next unless $repo =~ $REPONAME_PATT; - next if $repo =~ m(^\@|EXTCMD/); # these are not real repos - my $export_ok = "$repo.git/git-daemon-export-ok"; - if ($repos{$repo}{'R'}{'daemon'}) { - system("touch $export_ok"); - } else { - unlink($export_ok); - } - } - - my %projlist = (); - # ...then gitwebs - for my $repo (sort keys %repos) { - next unless $repo =~ $REPONAME_PATT; - next if $repo =~ m(^\@|EXTCMD/); # these are not real repos - my $desc_file = "$repo.git/description"; - # note: having a description also counts as enabling gitweb - if ($repos{$repo}{'R'}{'gitweb'} or $desc{"$repo.git"}) { - $projlist{"$repo.git"} = 1; - # add the description file; no messages to user or error checking :) - $desc{"$repo.git"} and open(DESC, ">", $desc_file) and print DESC $desc{"$repo.git"} . "\n" and close DESC; - if ($owner{"$repo.git"}) { - # set the repository owner - system("git", "--git-dir=$repo.git", "config", "gitweb.owner", $owner{"$repo.git"}); - } else { - # remove the repository owner setting - system("git --git-dir=$repo.git config --unset-all gitweb.owner 2>/dev/null"); - } - } else { - # delete the description file; no messages to user or error checking :) - unlink $desc_file; - # remove the repository owner setting - system("git --git-dir=$repo.git config --unset-all gitweb.owner 2>/dev/null"); - } - - # unless there are other gitweb.* keys set, remove the section to keep the - # config file clean - my $keys = `git --git-dir=$repo.git config --get-regexp '^gitweb\\.' 2>/dev/null`; - if (length($keys) == 0) { - system("git --git-dir=$repo.git config --remove-section gitweb 2>/dev/null"); - } - } - - # update the project list - my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); - for my $proj (sort keys %projlist) { - print $projlist_fh "$proj\n"; - } - close $projlist_fh; +# write out the project list +my $projlist_fh = wrap_open( ">", $PROJECTS_LIST); +for my $proj (sort keys %projlist) { + print $projlist_fh "$proj\n"; } +close $projlist_fh; # ---------------------------------------------------------------------------- # "compile" ssh authorized_keys From 8deee9b6bd60ae279caacceb11eb0bd8b0d67980 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 19 Jul 2010 14:45:43 +0530 Subject: [PATCH 437/850] (tests) added t57 for daemon and gitweb stuff --- t/t57-daemon-gitweb | 115 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 t/t57-daemon-gitweb diff --git a/t/t57-daemon-gitweb b/t/t57-daemon-gitweb new file mode 100644 index 0000000..7b501e3 --- /dev/null +++ b/t/t57-daemon-gitweb @@ -0,0 +1,115 @@ +# vim: syn=sh: +for wr in 0 1 +do + for bc in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS $wr + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + @gbar = bar + repo @gbar + RW+ = @leads + RW = @devs + " | ugc + expect_push_ok "master -> master" + runremote ls -al repositories/bar.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + expect "testing.git" + + name "add daemon access to bar" + echo " + R = daemon + " | ugc + runremote ls -al repositories/bar.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar.git/git-daemon-export-ok" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 12 .* projects.list" + + name "add foo with gitweb access" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + @gfoo = foo + repo @gfoo + RW+ = @leads + RW = @devs + R = gitweb + " | ugc + expect_push_ok "master -> master" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 20 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "foo.git" + + name "add gitweb access to bar" + echo " + repo @gbar + R = gitweb + " | ugc + expect_push_ok "master -> master" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "foo.git" + expect "bar.git" + + name "add repo baz with description" + echo " + @gbaz = baz + repo @gbaz + RW = @leads + + baz = \"this is repo baz\" + " | ugc + expect_push_ok "master -> master" + expect "remote: Initialized empty Git repository in /home/gitolite-test/repositories/baz.git/" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 36 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "foo.git" + expect "bar.git" + expect "baz.git" + runremote ls -al repositories/baz.git/description + expect "gitolite-test gitolite-test 17 .* repositories/baz.git/description" + runremote cat repositories/baz.git/description + expect "this is repo baz" + + name "add owner to testing repo" + echo " + testing \"sitaram\" = \"this is the testing repo\" + " | ugc + expect_push_ok "master -> master" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 36 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "foo.git" + expect "bar.git" + expect "baz.git" + runremote cat repositories/testing.git/config + expect "\[gitweb\]" + expect "owner = sitaram" + runremote ls -al repositories/testing.git/description + expect "gitolite-test gitolite-test 25 .* repositories/testing.git/description" + runremote cat repositories/testing.git/description + expect "this is the testing repo" + + name INTERNAL + done +done From f21e7780a16b1864a2110cfac5f43ce3fbaee47a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 19 Jul 2010 16:47:38 +0530 Subject: [PATCH 438/850] fix up gitweb, daemon, for wild + big-config Implementation notes: - %repo_config is now "our", not "my" - collect_repo_patts now uses repo_rights to get the name of the wild card repo (if any) that pertains to the physical $repo, instead of all that new code (duh!) - new "can_read(repo, user)" sub (to help daemon and gitweb use) - the "convenience copy on steroids" thing now copies %repo_config also, not just %repos. This makes setup_repo_configs simpler - $creator gets substituted into %groups also; we need that now that we (%repos and %groups) are working closer together :) --- src/gitolite.pm | 53 +++++++++++++++++++++++---------------------- src/gl-auth-command | 7 +++--- src/gl-compile-conf | 50 +++++++++++++++++++++++------------------- 3 files changed, 58 insertions(+), 52 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index bf70db6..1a3bfb6 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -40,6 +40,7 @@ our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG); our %repos; our %groups; +our %repo_config; our $data_version; our $current_data_version = '1.5'; @@ -129,24 +130,9 @@ sub collect_repo_patts for my $repo (`find . -type d -name "*.git"`) { chomp ($repo); $repo =~ s(\./(.*)\.git$)($1); - # if its non-wild that's all you need - if ($repos_p->{$repo}) { - $repo_patts{$repo} = $repo; - } else { - # otherwise it gets a wee bit complicated ;-) - chomp (my $creator = `cat $repo.git/gl-creater`); - for my $key (keys %$repos_p) { - my $key2 = $key; - # subst $creator in the copy with the creator name - $key2 =~ s/\$creator/$creator/g; - # match the new key against $repo - if ($repo =~ /^$key2$/) { - # and if it matched you're done for this $repo - $repo_patts{$repo} = $key; - last; - } - } - } + # the key has to be in the list, since the repo physically exists + my($perm, $creator, $wild) = &repo_rights($repo); + $repo_patts{$repo} = $wild || $repo; } return %repo_patts; @@ -290,9 +276,9 @@ sub get_set_desc sub setup_repo_configs { - my ($repo, $repo_patt, $repo_config_p) = @_; + my ($repo, $repo_config_p) = @_; - while ( my ($key, $value) = each(%{ $repo_config_p->{$repo_patt} }) ) { + while ( my ($key, $value) = each(%{ $repo_config_p->{$repo} }) ) { if ($value) { $value =~ s/^"(.*)"$/$1/; system("git", "config", $key, $value); @@ -306,12 +292,13 @@ sub setup_repo_configs # set/unset daemon access # ---------------------------------------------------------------------------- +# does not return anything; just touch/unlink the appropriate file my $export_ok = "git-daemon-export-ok"; sub setup_daemon_access { - my ($repo, $allowed) = @_; + my $repo = shift; - if ($allowed) { + if (&can_read($repo, 'daemon')) { system("touch $export_ok"); } else { unlink($export_ok); @@ -322,13 +309,18 @@ sub setup_daemon_access # set/unset gitweb access # ---------------------------------------------------------------------------- +# returns 1 if gitweb access has happened; this is to allow the caller to add +# an entry to the projects.list file my $desc_file = "description"; sub setup_gitweb_access # this also sets "owner" for gitweb, by the way { - my ($repo, $allowed, $desc, $owner) = @_; + my ($repo, $desc, $owner) = @_; + my $ret = 0; - if ($allowed) { + # passing in a descr implies 'R = gitweb' + if ($desc or &can_read($repo, 'gitweb')) { + $ret = 1; if ($desc) { open(DESC, ">", $desc_file); print DESC $desc . "\n"; @@ -351,6 +343,8 @@ sub setup_gitweb_access if (length($keys) == 0) { system("git config --remove-section gitweb 2>/dev/null"); } + + return $ret; } # ---------------------------------------------------------------------------- @@ -409,6 +403,7 @@ sub parse_acl $repos{$dr}{DELETE_IS_D} = 1 if $repos{$r}{DELETE_IS_D}; $repos{$dr}{CREATE_IS_C} = 1 if $repos{$r}{CREATE_IS_C}; $repos{$dr}{NAME_LIMITS} = 1 if $repos{$r}{NAME_LIMITS}; + $repo_config{$dr} = $repo_config{$r} if $repo_config{$r}; for my $u ('@all', "$gl_user - wild", @user_plus) { my $du = $gl_user; $du = '@all' if $u eq '@all'; @@ -522,8 +517,6 @@ sub expand_wild $repo =~ s/^\.\///; $repo =~ s/\.git$//; - return if $last_repo eq $repo; # a wee bit o' caching, though not yet needed - # we get passed an actual repo name. It may be a normal # (non-wildcard) repo, in which case it is assumed to exist. If it's # a wildrepo, it may or may not exist. If it doesn't exist, the "C" @@ -582,6 +575,14 @@ sub cli_repo_rights { print "$perm $creator\n"; } +sub can_read { + my $repo = shift; + my $user = shift || $ENV{GL_USER}; + local $ENV{GL_USER} = $user; + my ($perm, $creator, $wild) = &repo_rights($repo); + return $perm =~ /R/; +} + # ---------------------------------------------------------------------------- # setup the ~/.ssh/authorized_keys file # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index bd7737a..9a1e0dc 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -207,10 +207,9 @@ if ($perm =~ /C/) { # it was missing, and you have create perms wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); - &setup_repo_configs($repo, $ENV{GL_REPOPATT}, \%repo_config); - &setup_daemon_access($repo, $repos{$ENV{GL_REPOPATT}}{'R'}{'daemon'} || ''); - &setup_gitweb_access($repo, $repos{$ENV{GL_REPOPATT}}{'R'}{'gitweb'} || '', '', ''); - system("echo $repo.git >> $PROJECTS_LIST"); + &setup_repo_configs($repo, \%repo_config); + &setup_daemon_access($repo); + system("echo $repo.git >> $PROJECTS_LIST") if &setup_gitweb_access($repo, '', ''); wrap_chdir($ENV{HOME}); } diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 8926a91..0a93465 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -105,7 +105,7 @@ our $current_data_version; # this comes from gitolite.pm my %user_list = (); # repo configurations -my %repo_config = (); +our %repo_config = (); # gitweb descriptions and owners; plain text, keyed by "$repo.git" my %desc = (); @@ -311,7 +311,15 @@ sub parse_conf_file for my $repo (@repos) # each repo in the current stanza { $repo_config{$repo}{$key} = $value; - print STDERR "$WARN git config set for $repo but \$GL_GITCONFIG_WILD not set\n" unless $repo =~ $REPONAME_PATT or $GL_GITCONFIG_WILD; + # no problem if it's a plain repo (non-pattern, non-groupname) + # OR wild configs are allowed + unless ( ($repo =~ $REPONAME_PATT and $repo !~ /^@/) or $GL_GITCONFIG_WILD) { + my @r = ($repo); # single wildpatt + @r = sort keys %{ $groups{$repo} } if $groups{$repo}; # or a group; get its members + do { + print STDERR "$WARN git config set for $_ but \$GL_GITCONFIG_WILD not set\n" unless $_ =~ $REPONAME_PATT + } for @r; + } } } # include @@ -389,7 +397,12 @@ $dumped_data .= Data::Dumper->Dump([\%repo_config], [qw(*repo_config)]) if %repo # much... $dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g; print $compiled_fh $dumped_data; -print $compiled_fh Data::Dumper->Dump([\%groups], [qw(*groups)]) if $GL_BIG_CONFIG and %groups; +if ($GL_BIG_CONFIG and %groups) { + $dumped_data = Data::Dumper->Dump([\%groups], [qw(*groups)]); + $dumped_data =~ s/\bCREAT[EO]R\b/\$creator/g; + $dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g; + print $compiled_fh $dumped_data; +} close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n"; rename "$GL_CONF_COMPILED.new", "$GL_CONF_COMPILED"; @@ -504,23 +517,12 @@ my %projlist = (); # for each real repo (and remember this will be empty, thus skipping all this, # if $GL_NO_DAEMON_NO_GITWEB is on!) for my $repo (keys %repo_patts) { - my $repo_patt = $repo_patts{$repo}; # if non-wild, $repo_patt will be eq $repo anyway + my $repo_patt = $repo_patts{$repo}; wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git"); - # git config - if ($repo_patt eq $repo or $GL_GITCONFIG_WILD) { - # erm, what that means is that it's either a non-wild repo being - # config'd or a wild one but gitconfig is allowed on wilds. - # XXX do we really need GL_GITCONFIG_WILD now? It was meant to be - # only an efficiency thing, but that was before this whole revamp; - # we already trawl through $REPO_BASE exactly once now anyway! - # ...need to think about this - &setup_repo_configs($repo, $repo_patt, \%repo_config) if $repo_config{$repo_patt}; - } - # daemon is easy - &setup_daemon_access($repo, $repos{$repo_patt}{'R'}{'daemon'} || ''); + &setup_daemon_access($repo); # gitweb is a little more complicated. Here're some notes: # - "setup_gitweb_access" also sets "owner", despite the name @@ -532,12 +534,16 @@ for my $repo (keys %repo_patts) { # into the "repo foo" section; they're essentialy independent. # Anyway, I believe it doesn't make sense to have all wild repos # (for some pattern) to have the same description and owner. - if ($repos{$repo_patt}{'R'}{'gitweb'} or $desc{"$repo.git"}) { - $projlist{"$repo.git"} = 1; - &setup_gitweb_access($repo, 1, $desc{"$repo.git"} || '', $owner{"$repo.git"} || ''); - } else { - &setup_gitweb_access($repo, 0, '', ''); - } + $projlist{"$repo.git"} = 1 if &setup_gitweb_access($repo, $desc{"$repo.git"} || '', $owner{"$repo.git"} || ''); + + # git config + # implementation note: this must happen *after* one of the previous 2 + # calls (setup daemon or gitweb). The reason is that they call + # "can_read", which eventually calls parse_acl with the right "creator" + # set for the *current* repo, which in turn stores translated values fr + # $creator in the repo_config hash, which, (phew!) is needed for a match + # that eventually gets you a valid $repo_config{} below + &setup_repo_configs($repo, \%repo_config) if $repo_config{$repo}; } # write out the project list From 204b34e52547bb51ac60c3e4bcddf10cbb4d5df3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 24 Jul 2010 19:23:31 +0530 Subject: [PATCH 439/850] lots of changes to tests for big-config as well as wild, and (as much as I could) both together --- t/t03a-branch-permissions | 11 +- t/t04a-wild-all | 3 +- t/t04a-wild-students | 3 +- t/t50-sequence-test | 6 +- t/t51-personal-branches | 3 +- t/t52-deny-create-ref | 6 +- t/t53-check-info-expand-output | 7 +- t/t54-repo-configs | 12 +- t/t55-repo-configs-wild-without-CREATOR | 211 ++++++++++++------------ t/t58-daemon-gitweb-wild | 127 ++++++++++++++ 10 files changed, 271 insertions(+), 118 deletions(-) create mode 100644 t/t58-daemon-gitweb-wild diff --git a/t/t03a-branch-permissions b/t/t03a-branch-permissions index f8a3452..39dd8ab 100644 --- a/t/t03a-branch-permissions +++ b/t/t03a-branch-permissions @@ -5,7 +5,8 @@ echo " @g1 = u1 @g2 = u2 @g3 = u3 - repo aa + @gaa = aa + repo @gaa RW+ = @g1 RW = @g2 RW+ master = @g3 @@ -116,7 +117,7 @@ expect " * \[new branch\] dev -> dev" # u1 tries to delete dev on a new setup name "INTERNAL" echo " - repo aa + repo @gaa RWD dev = u4 " | ugc expect "master -> master" @@ -135,7 +136,8 @@ expect " - \[deleted\] *dev" name "INTERNAL" echo " -repo r1 +@gr1 = r1 +repo @gr1 RW refs/heads/v[0-9] = u1 RW refs/heads = tester " | ugc @@ -155,7 +157,8 @@ expect "\* \[new branch\] v1 -> v1" name "INTERNAL" echo " -repo r2 +@gr2 = r2 +repo @gr2 RW refs/heads/v[0-9] = u1 - refs/heads/v[0-9] = tester RW refs/heads = tester diff --git a/t/t04a-wild-all b/t/t04a-wild-all index 7bcefab..1b6ebf0 100644 --- a/t/t04a-wild-all +++ b/t/t04a-wild-all @@ -7,7 +7,8 @@ echo " @TAs = u2 u3 @students = u4 u5 u6 - repo foo/CREATOR/a[0-9][0-9] + @gfoo = foo/CREATOR/a[0-9][0-9] + repo @gfoo C = @all RW+ = CREATOR RW = WRITERS @TAs diff --git a/t/t04a-wild-students b/t/t04a-wild-students index 0fdc85c..ad674ad 100644 --- a/t/t04a-wild-students +++ b/t/t04a-wild-students @@ -7,7 +7,8 @@ echo " @TAs = u2 u3 @students = u4 u5 u6 - repo foo/CREATOR/a[0-9][0-9] + @gfoo = foo/CREATOR/a[0-9][0-9] + repo @gfoo C = @students RW+ = CREATOR RW = WRITERS @TAs diff --git a/t/t50-sequence-test b/t/t50-sequence-test index 0a33eb7..f90518d 100644 --- a/t/t50-sequence-test +++ b/t/t50-sequence-test @@ -11,7 +11,8 @@ do name "INTERNAL" echo " @staff = u1 u2 u3 - repo foo/CREATOR/.+ + @gfoo = foo/CREATOR/.+ + repo @gfoo C = u1 RW+ = CREATOR RW = WRITERS @@ -57,7 +58,8 @@ do name "INTERNAL" echo " @staff = u1 u2 u3 - repo foo/CREATOR/.+ + @gfoo = foo/CREATOR/.+ + repo @gfoo C = u1 RW+ = CREATOR - = @staff diff --git a/t/t51-personal-branches b/t/t51-personal-branches index d040bc4..314cd6c 100644 --- a/t/t51-personal-branches +++ b/t/t51-personal-branches @@ -13,7 +13,8 @@ do name "INTERNAL" echo " @staff = u1 u2 u3 u4 u5 u6 - repo foo + @gfoo = foo + repo @gfoo RW+ = u1 u2 RW+ personal/USER/ = u3 u4 RW temp = u5 u6 diff --git a/t/t52-deny-create-ref b/t/t52-deny-create-ref index 8e32a36..a56be28 100644 --- a/t/t52-deny-create-ref +++ b/t/t52-deny-create-ref @@ -14,7 +14,8 @@ do @leads = u1 u2 @devs = u1 u2 u3 u4 - repo foo + @gfoo = foo + repo @gfoo RW+C = @leads RW+C personal/USER/ = @devs RW = @devs @@ -74,7 +75,8 @@ do @leads = u1 u2 @devs = u1 u2 u3 u4 - repo bar + @gbar = bar + repo @gbar RW+ = @leads RW+ personal/USER/ = @devs RW = @devs diff --git a/t/t53-check-info-expand-output b/t/t53-check-info-expand-output index ad845b7..563d4bd 100644 --- a/t/t53-check-info-expand-output +++ b/t/t53-check-info-expand-output @@ -14,7 +14,8 @@ do echo " @leads = u1 u2 @devs = u1 u2 u3 u4 - repo foo/CREATOR/.+ + @gfoo = foo/CREATOR/.+ + repo @gfoo C = @leads RW+ = CREATOR " | ugc @@ -36,7 +37,9 @@ do runlocal ssh u1 info expect "hello u1, the gitolite version here is" expect "@R.*@W.*testing" - [ "$wr" = "1" ] && expect "C.*R.*W.*foo/u1/\\.\\+" + [ "$wr" = "1" ] && [ "$bc" = "0" ] && expect "C.*R.*W.*foo/u1/\\.\\+" + [ "$wr" = "1" ] && [ "$bc" = "1" ] && notexpect "C.*R.*W.*foo/u1/\\.\\+" + [ "$wr" = "1" ] && [ "$bc" = "1" ] && expect "C.*@gfoo" name "u1 expand" runlocal ssh u1 expand diff --git a/t/t54-repo-configs b/t/t54-repo-configs index 4e53921..e14051f 100644 --- a/t/t54-repo-configs +++ b/t/t54-repo-configs @@ -15,7 +15,8 @@ do @leads = u1 u2 @devs = u1 u2 u3 u4 - repo bar + @gbar = bar + repo @gbar RW+ = @leads RW = @devs config foo.bar = baz @@ -38,7 +39,8 @@ do @leads = u1 u2 @devs = u1 u2 u3 u4 - repo bar + @gbar = bar + repo @gbar RW+ = @leads RW = @devs config foo.bar = baz @@ -55,7 +57,8 @@ do @leads = u1 u2 @devs = u1 u2 u3 u4 - repo bar + @gbar = bar + repo @gbar RW+ = @leads RW = @devs config foo.bar = baz @@ -73,7 +76,8 @@ do @leads = u1 u2 @devs = u1 u2 u3 u4 - repo bar + @gbar = bar + repo @gbar RW+ = @leads RW = @devs config foo.bar = diff --git a/t/t55-repo-configs-wild-without-CREATOR b/t/t55-repo-configs-wild-without-CREATOR index 68303cf..256c25c 100644 --- a/t/t55-repo-configs-wild-without-CREATOR +++ b/t/t55-repo-configs-wild-without-CREATOR @@ -1,126 +1,135 @@ # vim: syn=sh: -for gcw in 0 1 +for bc in 0 1 do - cd $TESTDIR - $TESTDIR/rollback || die "rollback failed" + for gcw in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" - name INTERNAL - editrc GL_WILDREPOS 1 - echo "\$GL_GITCONFIG_WILD = $gcw;" | addrc + name INTERNAL + editrc GL_WILDREPOS 1 + editrc GL_BIG_CONFIG $bc + echo "\$GL_GITCONFIG_WILD = $gcw;" | addrc - # ---------- + # ---------- - name "fail to set foo.bar" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 + name "fail to set foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - repo bar/..* - C = @leads - RW+ = CREATOR - RW = @leads - config foo.bar = baz - " | ugc - expect "remote: git config foo.bar not allowed" + @gbar = bar/..* + repo @gbar + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = baz + " | ugc + expect "remote: git config foo.bar not allowed" - name "update rc file to allow foo.*" - catrc - cp ~/1 ~/junk - perl -pi -e 's/GL_GITCONFIG_KEYS = ""/GL_GITCONFIG_KEYS = "foo\\\\..*"/' ~/junk - cat ~/junk | runremote dd of=.gitolite.rc - catrc - expect "GL_GITCONFIG_KEYS.*foo" + name "update rc file to allow foo.*" + catrc + cp ~/1 ~/junk + perl -pi -e 's/GL_GITCONFIG_KEYS = ""/GL_GITCONFIG_KEYS = "foo\\\\..*"/' ~/junk + cat ~/junk | runremote dd of=.gitolite.rc + catrc + expect "GL_GITCONFIG_KEYS.*foo" - name "ok to set foo.bar" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 + name "ok to set foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - repo bar/..* - C = @leads - RW+ = CREATOR - RW = @leads - config foo.bar = baz - " | ugc -r - [ "$gcw" = "0" ] && expect "remote: git config set for bar/..\* but \$GL_GITCONFIG_WILD not set" - [ "$gcw" = "1" ] && notexpect "remote: git config set for bar/..\* but \$GL_GITCONFIG_WILD not set" - notexpect "git config.*not allowed" - expect_push_ok "master -> master" + @gbar = bar/..* + repo @gbar + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = baz + " | ugc -r + [ "$gcw" = "0" ] && expect "remote: git config set for bar/..\* but \$GL_GITCONFIG_WILD not set" + [ "$gcw" = "1" ] && notexpect "remote: git config set for bar/..\* but \$GL_GITCONFIG_WILD not set" + notexpect "git config.*not allowed" + expect_push_ok "master -> master" - [ "$gcw" = "0" ] && continue # the rest of these tests don't make sense now + [ "$gcw" = "0" ] && continue # the rest of these tests don't make sense now - name "fail to set foobar.baz" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 + name "fail to set foobar.baz" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - repo bar/..* - C = @leads - RW+ = CREATOR - RW = @leads - config foo.bar = baz - config foobar.baz = ooka - " | ugc -r - expect "remote: git config foobar.baz not allowed" + @gbar = bar/..* + repo @gbar + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = baz + config foobar.baz = ooka + " | ugc -r + expect "remote: git config foobar.baz not allowed" - name "u1 create bar/try1" - runlocal git ls-remote u1:bar/try1 - expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/try1.git/" + name "u1 create bar/try1" + runlocal git ls-remote u1:bar/try1 + expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/try1.git/" - name "check u1 has foo.bar" - runremote cat repositories/bar/try1.git/config - expect '^.foo' - expect 'bar = baz' + name "check u1 has foo.bar" + runremote cat repositories/bar/try1.git/config + expect '^.foo' + expect 'bar = baz' - name "delete foo.bar" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 + name "delete foo.bar" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - repo bar/..* - C = @leads - RW+ = CREATOR - RW = @leads - config foo.bar = - " | ugc -r - expect_push_ok "master -> master" + @gbar = bar/..* + repo @gbar + C = @leads + RW+ = CREATOR + RW = @leads + config foo.bar = + " | ugc -r + expect_push_ok "master -> master" - name "check u1 doesnt have foo.bar" - runremote cat repositories/bar/try1.git/config - expect '^.foo' # git leaves the section header behind - notexpect 'bar = baz' + name "check u1 doesnt have foo.bar" + runremote cat repositories/bar/try1.git/config + expect '^.foo' # git leaves the section header behind + notexpect 'bar = baz' - name "u2 create bar/try2" - runlocal git ls-remote u2:bar/try2 - expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/try2.git/" + name "u2 create bar/try2" + runlocal git ls-remote u2:bar/try2 + expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/try2.git/" - name "check u2 doesnt have foo.bar" - runremote cat repositories/bar/try2.git/config - notexpect '^.foo' # but not here, since this repo never had the key at all - notexpect 'bar = baz' + name "check u2 doesnt have foo.bar" + runremote cat repositories/bar/try2.git/config + notexpect '^.foo' # but not here, since this repo never had the key at all + notexpect 'bar = baz' - name "add foo.frob retroactively" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 + name "add foo.frob retroactively" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - repo bar/..* - C = @leads - RW+ = CREATOR - RW = @leads - config foo.frob = nitz - " | ugc -r - expect_push_ok "master -> master" + @gbar = bar/..* + repo @gbar + C = @leads + RW+ = CREATOR + RW = @leads + config foo.frob = nitz + " | ugc -r + expect_push_ok "master -> master" - name "check u1 has foo.frob" - runremote cat repositories/bar/try1.git/config - expect '^.foo' - expect 'frob = nitz' + name "check u1 has foo.frob" + runremote cat repositories/bar/try1.git/config + expect '^.foo' + expect 'frob = nitz' - name "check u2 has foo.frob" - runremote cat repositories/bar/try2.git/config - expect '^.foo' - expect 'frob = nitz' + name "check u2 has foo.frob" + runremote cat repositories/bar/try2.git/config + expect '^.foo' + expect 'frob = nitz' - name INTERNAL + name INTERNAL + done done diff --git a/t/t58-daemon-gitweb-wild b/t/t58-daemon-gitweb-wild new file mode 100644 index 0000000..6b80b62 --- /dev/null +++ b/t/t58-daemon-gitweb-wild @@ -0,0 +1,127 @@ +# vim: syn=sh: +for bc in 0 1 +do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS 1 + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + @gbar = bar/CREATOR/..* + repo @gbar + C = @leads + RW+ = @leads + RW = @devs + " | ugc + expect_push_ok "master -> master" + runlocal git ls-remote u1:bar/u1/try1 + runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try1.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + expect "testing.git" + notexpect "bar/u1/try1.git" + + name "add daemon access" + echo " + R = daemon + " | ugc + runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try1.git/git-daemon-export-ok" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 12 .* projects.list" + + name "add one more repo" + runlocal git ls-remote u1:bar/u1/try2 + runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try2.git/git-daemon-export-ok" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + expect "testing.git" + notexpect "bar/u1/try1.git" + notexpect "bar/u1/try2.git" + + name "add descriptions for try1 and try3 and compile" + echo " + bar/u1/try1 = \"this is bar/u1/try1\" + bar/u1/try3 = \"this is bar/u1/try3\" + " | ugc + runremote ls -al projects.list + expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "bar/u1/try1.git" + notexpect "bar/u1/try2.git" + notexpect "bar/u1/try3.git" + runremote cat repositories/bar/u1/try1.git/description + expect "this is bar/u1/try1" + + name "add try3 project" + runlocal git ls-remote u1:bar/u1/try3 + runremote ls -al repositories/bar/u1/try3.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try3.git/git-daemon-export-ok" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "bar/u1/try1.git" + notexpect "bar/u1/try2.git" + notexpect "bar/u1/try3.git" + runremote cat repositories/bar/u1/try1.git/description + expect "this is bar/u1/try1" + runremote cat repositories/bar/u1/try3.git/description + expect "cat: repositories/bar/u1/try3.git/description: No such file or directory" + + name "now compile and recheck try3 stuff" + echo " + " | ugc + runremote ls -al repositories/bar/u1/try3.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try3.git/git-daemon-export-ok" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 44 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "bar/u1/try1.git" + notexpect "bar/u1/try2.git" + expect "bar/u1/try3.git" + runremote cat repositories/bar/u1/try1.git/description + expect "this is bar/u1/try1" + runremote cat repositories/bar/u1/try3.git/description + expect "this is bar/u1/try3" + + name "add owner for try2 and compile" + echo " + bar/u1/try2 \"owner2\" = \"this is bar/u1/try1\" + " | ugc + runremote cat repositories/bar/u1/try2.git/config + expect "\[gitweb\]" + expect "owner = owner2" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 60 .* projects.list" + runremote cat projects.list + expect "bar/u1/try2.git" + + name "add gitweb access to all" + echo " + repo @gbar + R = gitweb + " | ugc + expect_push_ok "master -> master" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 60 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "bar/u1/try1.git" + expect "bar/u1/try2.git" + expect "bar/u1/try3.git" + + name "INTERNAL" +done From a5601970da96b1d25c55996ab244acf46efbb0a3 Mon Sep 17 00:00:00 2001 From: "Kevin P. Fleming" Date: Tue, 27 Jul 2010 17:00:36 -0500 Subject: [PATCH 440/850] make repo patterns work in fragment-named groups --- src/gl-compile-conf | 52 +++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 0a93465..0ac20a3 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -144,6 +144,25 @@ sub expand_list # "compile" GL conf # ---------------------------------------------------------------------------- +sub check_fragment_repo_disallowed +{ + # trying to set access for $repo (='foo')... + my ($fragment, $repo) = @_; + + # processing the master config, not a fragment + return 0 if $fragment eq 'master'; + # fragment is also called 'foo' (you're allowed to have a + # fragment that is only concerned with one repo) + return 0 if $fragment eq $repo; + # same thing in big-config-land; foo is just @foo now + return 0 if $GL_BIG_CONFIG and ("\@$fragment" eq $repo); + my @matched = grep { $repo =~ /^$_$/ } + grep { $groups{"\@$fragment"}{$_} eq 'master' } + sort keys %{ $groups{"\@$fragment"} }; + return 0 if @matched > 0; + return 1; +} + sub parse_conf_file { my ($conffile, $fragment) = @_; @@ -234,32 +253,10 @@ sub parse_conf_file # rights on stuff outside his domain # trying to set access for $repo (='foo')... - if ( - # processing the master config, not a fragment - ( $fragment eq 'master' ) or - # fragment is also called 'foo' (you're allowed to have a - # fragment that is only concerned with one repo) - ( $fragment eq $repo ) or - # same thing in big-config-land; foo is just @foo now - ( $GL_BIG_CONFIG and "\@$fragment" eq $repo ) or - # fragment is called "bar" and "@bar = foo" has been - # defined in the master config - ( ($groups{"\@$fragment"}{$repo} || '') eq 'master' ) - ) { - # all these are fine - } else { - # this is a little more complex - - # fragment is called "bar", one or more "@bar = regex" - # have been specified in master, and "foo" matches some - # such "regex" - my @matched = grep { $repo =~ /^$_$/ } - grep { $groups{"\@$fragment"}{$_} eq 'master' } - sort keys %{ $groups{"\@$fragment"} }; - if (@matched < 1) { - $ignored{$fragment}{$repo} = 1; - next; - } + if (check_fragment_repo_disallowed($fragment, $repo)) + { + $ignored{$fragment}{$repo} = 1; + next; } for my $user (@users) { @@ -339,8 +336,7 @@ sub parse_conf_file { my ($repo, $owner, $desc) = ($1, $2, $3); die "$ABRT bad repo name $repo\n" unless $repo =~ $REPONAME_PATT; - die "$WARN $fragment attempting to set description for $repo\n" if - $fragment ne 'master' and $fragment ne $repo and ($groups{"\@$fragment"}{$repo} || '') ne 'master'; + die "$WARN $fragment attempting to set description for $repo\n" if check_fragment_repo_disallowed($fragment, $repo); $desc{"$repo.git"} = $desc; $owner{"$repo.git"} = $owner || ''; } From 79f0a5fd5238e4b873d6718420202bd55f2bfda1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 8 Aug 2010 22:08:46 +0530 Subject: [PATCH 441/850] (big one!) more than one wildcard may match a repo... plus it can also be matched by a normal repo line. In other words, with repo foo/bar RW = u1 repo foo/..* RW = u2 user u2 has access to foo/bar (the non-wild does not cause the wild to be completely ignored any longer) implementation notes: get_memberships: - no more highlander ("there can only be one") for patterns in @repo_plus - return $wild as a space-separated list of matched patterns collect_repo_patts: - as of the last change to this section of code it appears we weren't using the values anyway, but I had forgotten :-) repo_rights: (big change: $wild no longer implies $creator present, or vice versa) - new type of "creator" (like "was_sitaram") is now possible --- src/gitolite.pm | 85 +++++++++++++++++++++++---------------------- src/gl-compile-conf | 1 - 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 1a3bfb6..246e088 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -131,8 +131,15 @@ sub collect_repo_patts chomp ($repo); $repo =~ s(\./(.*)\.git$)($1); # the key has to be in the list, since the repo physically exists - my($perm, $creator, $wild) = &repo_rights($repo); - $repo_patts{$repo} = $wild || $repo; + # -- my($perm, $creator, $wild) = &repo_rights($repo); + # -- $repo_patts{$repo} = $wild || $repo; + # turns out we're not using the value anywhere, so no point wasting + # all those cycles getting all repos' rights, at least until a real + # use for it comes along. But when it does come along, remember that + # $wild is now a space separated list of matching patterns (or empty + # if no wild patterns matched $repo). It is NOT a single value + # anymore! + $repo_patts{$repo} = 1; } return %repo_patts; @@ -389,10 +396,6 @@ sub parse_acl # expand $repo and $gl_user into all possible matching values ($wild, @repo_plus) = &get_memberships($repo, 1); ( @user_plus) = &get_memberships($gl_user, 0); - # XXX testing notes: the above should return just one entry during - # non-BC usage, whether wild or not - die "assert 1 failed" if (@repo_plus > 1 and $repo_plus[-1] ne '@all' - or @repo_plus > 2) and not $GL_BIG_CONFIG; # the old "convenience copy" thing. Now on steroids :) @@ -544,11 +547,16 @@ sub expand_wild $wild = &parse_acl($GL_CONF_COMPILED, $repo, $ENV{GL_USER}, "NOBODY", "NOBODY"); } - if ($exists and not $wild) { - $creator = ''; - } elsif ($exists) { - # is a wildrepo, and it has already been created - $creator = "($creator)"; + if ($exists) { + if ($creator and $wild) { + $creator = "($creator)"; + } elsif ($creator and not $wild) { + # was created wild but then someone (a) removed the pattern + # from, and (b) added the actual reponame to, the config + $creator = "" + } else { + $creator = ""; + } } else { # repo didn't exist; C perms need to be filled in $perm = ( $repos{$repo}{C}{'@all'} ? ' @C' : ( $repos{$repo}{C}{$ENV{GL_USER}} ? ' =C' : ' ' )) if $GL_WILDREPOS; @@ -750,40 +758,39 @@ sub special_cmd # ---------------------------------------------------------------------------- # given a plain reponame or username, return: -# - the name itself, plus all the groups it belongs to if $GL_BIG_CONFIG is -# set -# OR -# - (for repos) if the name itself doesn't exist in the config, a wildcard -# matching it, plus all the groups that wildcard belongs to (again if -# $GL_BIG_CONFIG is set) +# - the name itself if it's a user +# - the name itself if it's a repo and the repo exists in the config +# plus, if $GL_BIG_CONFIG is set: +# - all the groups the name belongs to +# plus, for repos: +# - all the wildcards matching it +# plus, if $GL_BIG_CONFIG is set: +# - all the groups those wildcards belong to # A name can normally appear (repo example) (user example) -# - directly (repo foo) (RW = bar) +# - directly (repo foo) (RW = bob) # - (only for repos) as a direct wildcard (repo foo/.*) # but if $GL_BIG_CONFIG is set, it can also appear: -# - indirectly (@g = foo; repo @g) (@ug = bar; RW = @ug)) +# - indirectly (@g = foo; repo @g) (@ug = bob; RW = @ug)) # - (only for repos) as an indirect wildcard (@g = foo/.*; repo @g). -# things that may not be obvious from the above: -# - the wildcard stuff does not apply to username memberships -# - for repos, wildcard appearances are TOTALLY ignored if a non-wild -# appearance (direct or indirect) exists +# note: the wildcard stuff does not apply to username memberships sub get_memberships { my $base = shift; # reponame or username my $is_repo = shift; # some true value means a repo name has been passed - my $wild = ''; - my (@ret, @ret_w); # maintain wild matches separately from non-wild + my $wild = ''; # will be a space-sep list of matching patterns + my @ret; # list of matching groups/patterns # direct push @ret, $base if not $is_repo or exists $repos{$base}; - if ($is_repo and $GL_WILDREPOS and not @ret) { + if ($is_repo and $GL_WILDREPOS) { for my $i (sort keys %repos) { + next if $i eq $base; # "direct" name already done; skip + # direct wildcard if ($base =~ /^$i$/) { - die "$ABRT $base matches $wild AND $i\n" if $wild and $wild ne $i; - $wild = $i; - # direct wildcard - push @ret_w, $i; + push @ret, $i; + $wild = ($wild ? "$wild $i" : $i); } } } @@ -794,11 +801,10 @@ sub get_memberships { if ($base eq $i) { # indirect push @ret, $g; - } elsif ($is_repo and $GL_WILDREPOS and not @ret and $base =~ /^$i$/) { - die "$ABRT $base matches $wild AND $i\n" if $wild and $wild ne $i; - $wild = $i; + } elsif ($is_repo and $GL_WILDREPOS and $base =~ /^$i$/) { # indirect wildcard - push @ret_w, $g; + push @ret, $g; + $wild = ($wild ? "$wild $i" : $i); } } } @@ -813,14 +819,9 @@ sub get_memberships { return (@ret); } - # enforce the rule about ignoring all wildcard matches if a non-wild match - # exists while returning. (The @ret gating above does not adequately - # ensure this, it is only an optimisation). - # - # Also note that there is an extra return value when called for repos - # (compared to usernames) - - return ((@ret ? '' : $wild), (@ret ? @ret : @ret_w)); + # note that there is an extra return value when called for repos (as + # opposed to being called for usernames) + return ($wild, @ret); } # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 0ac20a3..99633e4 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -513,7 +513,6 @@ my %projlist = (); # for each real repo (and remember this will be empty, thus skipping all this, # if $GL_NO_DAEMON_NO_GITWEB is on!) for my $repo (keys %repo_patts) { - my $repo_patt = $repo_patts{$repo}; wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git"); From da210f21bdf7d117c439921b21c19ddad72dfa40 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 17 Aug 2010 21:35:54 +0530 Subject: [PATCH 442/850] log elapsed time I'm an idiot. I say I won't do it, then I go and do it anyway. Fortunately, in this case, the code and execution remain exactly the same for people who do not set $GL_PERFLOGT in the rc file, so it's tolerable. People who want even more than this can contact Greg Lonnon (see the mailing list archives at http://groups.google.com/group/gitolite for an obfuscated but easy to guess email address) ;-) --- conf/example.gitolite.rc | 11 +++++++++++ hooks/common/update | 2 +- src/gitolite.pm | 21 ++++++++++++++++++++- src/gl-auth-command | 16 ++-------------- src/gl-compile-conf | 2 +- src/gl-install | 2 +- src/gl-setup-authkeys | 2 ++ src/gl-time | 40 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 78 insertions(+), 18 deletions(-) create mode 100755 src/gl-time diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 263266d..82a9783 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -71,6 +71,17 @@ $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m.log"; # -------------------------------------- +# location of the performance log files + +# uncomment and set this variable if you want performance logging +# +# perf log files are different from access log files; they store different +# information, are not meant to be as long-lived, and so on + +# $GL_PERFLOGT="$GL_ADMINDIR/logs/perf-gitolite-%y-%m.log"; + +# -------------------------------------- + # Please DO NOT change these three paths $GL_CONF="$GL_ADMINDIR/conf/gitolite.conf"; diff --git a/hooks/common/update b/hooks/common/update index 5999449..93d3dac 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -25,7 +25,7 @@ use warnings; # common definitions # ---------------------------------------------------------------------------- -our ($GL_CONF_COMPILED, $UPDATE_CHAINS_TO); +our ($GL_CONF_COMPILED, $UPDATE_CHAINS_TO, $GL_PERFLOGT); our %repos; # people with shell access should be allowed to bypass the update hook, simply diff --git a/src/gitolite.pm b/src/gitolite.pm index 246e088..7dc2892 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -37,7 +37,7 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple patter our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file -our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG); +our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT); our %repos; our %groups; our %repo_config; @@ -64,6 +64,24 @@ sub dbg { } } +sub get_logfilename { + # this sub has a wee little side-effect; it sets $ENV{GL_TS} + my($template) = shift; + + my ($s, $min, $h, $d, $m, $y) = (localtime)[0..5]; + $y += 1900; $m++; # usual adjustments + for ($s, $min, $h, $d, $m) { + $_ = "0$_" if $_ < 10; + } + $ENV{GL_TS} = "$y-$m-$d.$h:$min:$s"; + + # substitute template parameters and set the logfile name + $template =~ s/%y/$y/g; + $template =~ s/%m/$m/g; + $template =~ s/%d/$d/g; + return ($template); +} + sub log_it { my ($ip, $logmsg); open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n"; @@ -608,6 +626,7 @@ sub setup_authkeys # command and options for authorized_keys my $AUTH_COMMAND="$bindir/gl-auth-command"; + $AUTH_COMMAND="$bindir/gl-time $bindir/gl-auth-command" if $GL_PERFLOGT; my $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; # START diff --git a/src/gl-auth-command b/src/gl-auth-command index 9a1e0dc..315482c 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -23,7 +23,7 @@ use warnings; # ---------------------------------------------------------------------------- # these are set by the "rc" file -our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $PROJECTS_LIST, $GL_SLAVE_MODE); +our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $PROJECTS_LIST, $GL_SLAVE_MODE, $GL_PERFLOGT); # and these are set by gitolite.pm our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); our %repos; @@ -82,19 +82,7 @@ $ENV{GL_GROUP_LIST} = join(" ", @ARGV) if @ARGV; # logging, timestamp env vars # ---------------------------------------------------------------------------- -# timestamp -my ($s, $min, $h, $d, $m, $y) = (localtime)[0..5]; -$y += 1900; $m++; # usual adjustments -for ($s, $min, $h, $d, $m) { - $_ = "0$_" if $_ < 10; -} -$ENV{GL_TS} = "$y-$m-$d.$h:$min:$s"; - -# substitute template parameters and set the logfile name -$GL_LOGT =~ s/%y/$y/g; -$GL_LOGT =~ s/%m/$m/g; -$GL_LOGT =~ s/%d/$d/g; -$ENV{GL_LOG} = $GL_LOGT; +$ENV{GL_LOG} = &get_logfilename($GL_LOGT); # ---------------------------------------------------------------------------- # sanity checks on SSH_ORIGINAL_COMMAND diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 99633e4..a128dc8 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_GITCONFIG_WILD, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB, $GL_NO_CREATE_REPOS, $GL_NO_SETUP_AUTHKEYS); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_GITCONFIG_WILD, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB, $GL_NO_CREATE_REPOS, $GL_NO_SETUP_AUTHKEYS, $GL_PERFLOGT); # and these are set by gitolite.pm our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $ABRT, $WARN); diff --git a/src/gl-install b/src/gl-install index ad51af3..db1affc 100755 --- a/src/gl-install +++ b/src/gl-install @@ -5,7 +5,7 @@ use strict; use warnings; -our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS); +our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $GL_PERFLOGT); # setup quiet mode if asked; please do not use this when running manually open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); diff --git a/src/gl-setup-authkeys b/src/gl-setup-authkeys index 377f798..758adac 100755 --- a/src/gl-setup-authkeys +++ b/src/gl-setup-authkeys @@ -24,6 +24,8 @@ # All in all, unless it is shown to be quite inefficient, I'd much # prefer processing *all* keys each time there is a change. +our ($GL_PERFLOGT); + # setup my $bindir = $0; $bindir =~ s/\/[^\/]+$//; diff --git a/src/gl-time b/src/gl-time new file mode 100755 index 0000000..d68df5e --- /dev/null +++ b/src/gl-time @@ -0,0 +1,40 @@ +#!/usr/bin/perl + +# this program is a performance measurement wrapper around anything that it is +# called with; it's arg-1 becomes the program being measured, with arg-2 +# onwards being arg-1's arguments + +# sorta like the "time" command... hence the name :-) + +use strict; +use warnings; + +use Time::HiRes qw(gettimeofday tv_interval); + +our ($GL_PERFLOGT); + +# rc file +do "$ENV{HOME}/.gitolite.rc"; + # this file is always in a fixed place; code in the main gitolite that + # seems to indicate it is not, is obsolete and needs to be fixed. + +# the common setup module is in the same directory as this running program is +my $bindir = $0; +$bindir =~ s/\/[^\/]+$//; +$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//; +require "$bindir/gitolite.pm"; + +# --------------------------------------------------------------- + +my $starttime = [gettimeofday]; + +my $pgm = shift; +my $returncode = system($pgm, @ARGV); +$returncode >>= 8; +$ENV{GL_USER} = shift; + +my $elapsedtime = tv_interval($starttime); + +$ENV{GL_LOG} = &get_logfilename($GL_PERFLOGT); +# log_it logs to $ENV{GL_LOG} +&log_it("", "$elapsedtime\trc=$returncode"); From 1b5294f26d5cbe396b0597b15e7a368257d60920 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 19 Aug 2010 20:19:16 +0530 Subject: [PATCH 443/850] make info and expand run faster using a wee bit of local optimisation in an inner function --- src/gitolite.pm | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 7dc2892..f749264 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -1,6 +1,7 @@ use strict; use Data::Dumper; $Data::Dumper::Deepcopy = 1; +$|++; # this file is commonly used using "require". It is not required to use "use" # (because it doesn't live in a different package) @@ -397,7 +398,17 @@ sub parse_acl our $writers = $ENV{GL_WRITERS} = $w || $ENV{GL_WRITERS} || "NOBODY"; our $gl_user = $ENV{GL_USER}; - die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; + # these need to persist across calls to this function, so "our" + our $saved_crwu; + our (%saved_repos, %saved_groups); + + if ($saved_crwu eq "$creator,$readers,$writers,$gl_user") { + %repos = %saved_repos; %groups = %saved_groups; + } else { + die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; + $saved_crwu = "$creator,$readers,$writers,$gl_user"; + %saved_repos = %repos; %saved_groups = %groups; + } unless (defined($data_version) and $data_version eq $current_data_version) { # this cannot happen for 'easy-install' cases, by the way... print STDERR "(INTERNAL: $data_version -> $current_data_version; running gl-setup)\n"; @@ -473,8 +484,8 @@ sub report_basic my $count = 0; for my $r (sort keys %repos) { next unless $r =~ /$repo/i; - # if $GL_BIG_CONFIG is on, limit the number of output lines to 5 - next if $GL_BIG_CONFIG and $count++ >= 5; + # if $GL_BIG_CONFIG is on, limit the number of output lines to 20 + next if $GL_BIG_CONFIG and $count++ >= 20; if ($r =~ $REPONAME_PATT and $r !~ /\bCREAT[EO]R\b/) { &parse_acl($GL_CONF_COMPILED, $r, "NOBODY", "NOBODY", "NOBODY"); } else { @@ -490,7 +501,7 @@ sub report_basic $perm .= ( $repos{$r}{W}{'@all'} ? ' @W' : ( $repos{'@all'}{W}{$user} ? ' #W' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ))); print "$perm\t$r\r\n" if $perm =~ /\S/; } - print "only 5 out of $count candidate repos examined\r\nplease use a partial reponame or regex pattern to limit output\r\n" if $GL_BIG_CONFIG and $count > 5; + print "only 20 out of $count candidate repos examined\r\nplease use a partial reponame or regex pattern to limit output\r\n" if $GL_BIG_CONFIG and $count > 20; } # ---------------------------------------------------------------------------- @@ -518,13 +529,13 @@ sub expand_wild $actual_repo =~ s/\.git$//; # actual_repo has to match the pattern being expanded next unless $actual_repo =~ /$repo/i; - next if $GL_BIG_CONFIG and $count++ >= 5; + next if $GL_BIG_CONFIG and $count++ >= 20; my($perm, $creator, $wild) = &repo_rights($actual_repo); next unless $perm =~ /\S/; print "$perm\t$creator\t$actual_repo\n"; } - print "only 5 out of $count candidate repos examined\nplease use a partial reponame or regex pattern to limit output\n" if $GL_BIG_CONFIG and $count > 5; + print "only 20 out of $count candidate repos examined\nplease use a partial reponame or regex pattern to limit output\n" if $GL_BIG_CONFIG and $count > 20; } # there will be multiple calls to repo_rights; better to use a closure. We From 38d5ab5e4798e99fa44ef7958caa68e447e9960b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 20 Aug 2010 09:45:05 +0530 Subject: [PATCH 444/850] (minor) rmrepo has 2 alternatives for who can delete --- contrib/adc/rmrepo | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contrib/adc/rmrepo b/contrib/adc/rmrepo index e1c288c..b35a422 100755 --- a/contrib/adc/rmrepo +++ b/contrib/adc/rmrepo @@ -5,7 +5,17 @@ delete=$1 get_rights_and_owner $delete + +# please choose one alternative and delete the other para + +# alternative 1 -- only creator can delete [ "$owner" = "$GL_USER" ] || die "$delete is not yours to delete!" +# alternative 2 -- creator or anyone with W access to admin repo can delete +[ "$owner" = "$GL_USER" ] || { + get_rights_and_owner gitolite-admin + [ -z "$perm_write" ] && die "$delete is not yours to delete and you're not the admin" +} + cd $GL_REPO_BASE_ABS rm -rf $delete.git From 0979f029270356c4373776188b4bc21ac27129ed Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 20 Aug 2010 10:33:22 +0530 Subject: [PATCH 445/850] expand t58 to check projects.list being created on create --- t/t58-daemon-gitweb-wild | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/t/t58-daemon-gitweb-wild b/t/t58-daemon-gitweb-wild index 6b80b62..cab1695 100644 --- a/t/t58-daemon-gitweb-wild +++ b/t/t58-daemon-gitweb-wild @@ -123,5 +123,18 @@ do expect "bar/u1/try2.git" expect "bar/u1/try3.git" + name "add try4 project" + runlocal git ls-remote u1:bar/u1/try4 + runremote ls -al projects.list + expect "gitolite-test gitolite-test 76 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "bar/u1/try1.git" + expect "bar/u1/try2.git" + expect "bar/u1/try3.git" + expect "bar/u1/try4.git" + runremote cat repositories/bar/u1/try4.git/description + expect "Unnamed repository; edit this file 'description' to name the repository." + name "INTERNAL" done From 648676faec315ca998796f86b18ac81a87169230 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 20 Aug 2010 20:55:23 +0530 Subject: [PATCH 446/850] gitweb and daemon should now work even from within setperms modifications: - call setup_gitweb_access and setup_daemon_access from with get_set_perms so when the user sets a perm explicitly it works - in setup_gitweb_access, do not delete description file or gitweb.owner if the repo is wild - make the "fork" adc set gitweb.owner *and* call setperms using GL_WILDREPOS_DEFPERMS - add tests bug fixes: - gl-auth did not even *look* at GL_WILDREPOS_DEFPERMS when auto-"C"reating a wild repo; fixed - setup_gitweb_access did not delete the description file as consistently as it deleted the owner what will NOT work: - removing gitweb permissions does not clear the name from "projects.list". That's complicated, so just wait till the next "compile" to make this happen (thanks to Jefferai for driving this...) ---- mildly puzzling: for some strange reason, after a "git ls-remote ...try3" in t58, instead of not creating a "description" file, we started seeing a 73-byte file containing this message: Unnamed repository; edit this file 'description' to name the repository. --- contrib/adc/fork | 5 +- src/gitolite.pm | 48 +++++++----- src/gl-auth-command | 2 + t/t58-daemon-gitweb-wild | 2 +- t/t60-daemon-gitweb-via-setperms | 128 +++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 22 deletions(-) create mode 100644 t/t60-daemon-gitweb-via-setperms diff --git a/contrib/adc/fork b/contrib/adc/fork index 3baab01..b3cac93 100755 --- a/contrib/adc/fork +++ b/contrib/adc/fork @@ -15,9 +15,12 @@ get_rights_and_owner $to git clone --bare -l $GL_REPO_BASE_ABS/$from.git $GL_REPO_BASE_ABS/$to.git [ $? -ne 0 ] && exit 1 -# fix up creator, and hooks +# fix up creator, gitweb owner, and hooks cd $GL_REPO_BASE_ABS/$to.git echo $GL_USER > gl-creater +git config gitweb.owner "$GL_USER" +( cd $HOME;perl -e 'do ".gitolite.rc"; print $GL_WILDREPOS_DEFPERMS' ) | + SSH_ORIGINAL_COMMAND="setperms $to" $GL_BINDIR/gl-auth-command $GL_USER cp -R $GL_REPO_BASE_ABS/$from.git/hooks/* $GL_REPO_BASE_ABS/$to.git/hooks if [ -n "$GL_WILDREPOS_DEFPERMS" ]; then diff --git a/src/gitolite.pm b/src/gitolite.pm index f749264..13a761d 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -38,7 +38,7 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple patter our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file -our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT); +our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST); our %repos; our %groups; our %repo_config; @@ -59,6 +59,13 @@ sub wrap_open { return $fh; } +sub wrap_print { + my ($file, $text) = @_; + my $fh = wrap_open(">", $file); + print $fh $text; + close($fh); +} + sub dbg { for my $i (@_) { print STDERR "DBG: $i\n"; @@ -269,6 +276,10 @@ sub get_set_perms system("cat > gl-perms"); print "New perms are:\n"; system("cat", "gl-perms"); + + # gitweb and daemon + setup_daemon_access($repo); + system("echo $repo.git >> $PROJECTS_LIST") if &setup_gitweb_access($repo, '', ''); } } @@ -342,26 +353,22 @@ sub setup_gitweb_access # this also sets "owner" for gitweb, by the way { my ($repo, $desc, $owner) = @_; - my $ret = 0; + my $is_wild = -f "gl-creater"; + # we may override but we do not remove gitweb.owner and description + # for wild repos - # passing in a descr implies 'R = gitweb' - if ($desc or &can_read($repo, 'gitweb')) { - $ret = 1; - if ($desc) { - open(DESC, ">", $desc_file); - print DESC $desc . "\n"; - close DESC; - } - if ($owner) { - # set the repository owner - system("git", "config", "gitweb.owner", $owner); - } else { - # remove the repository owner setting - system("git config --unset-all gitweb.owner 2>/dev/null"); - } + if ($desc) { + open(DESC, ">", $desc_file); + print DESC $desc . "\n"; + close DESC; } else { - unlink $desc_file; - system("git config --unset-all gitweb.owner 2>/dev/null"); + unlink $desc_file unless $is_wild; + } + + if ($owner) { + system("git", "config", "gitweb.owner", $owner); + } else { + system("git config --unset-all gitweb.owner 2>/dev/null") unless $is_wild; } # if there are no gitweb.* keys set, remove the section to keep the config file clean @@ -370,7 +377,8 @@ sub setup_gitweb_access system("git config --remove-section gitweb 2>/dev/null"); } - return $ret; + return ($desc or &can_read($repo, 'gitweb')); + # this return value is used by the caller to write to projects.list } # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index 315482c..9870b2d 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -195,6 +195,8 @@ if ($perm =~ /C/) { # it was missing, and you have create perms wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); + # note pwd is not the bare "repo.git"; new_repo does that... + wrap_print("gl-perms", "$GL_WILDREPOS_DEFPERMS\n") if $GL_WILDREPOS_DEFPERMS; &setup_repo_configs($repo, \%repo_config); &setup_daemon_access($repo); system("echo $repo.git >> $PROJECTS_LIST") if &setup_gitweb_access($repo, '', ''); diff --git a/t/t58-daemon-gitweb-wild b/t/t58-daemon-gitweb-wild index cab1695..67e2ba5 100644 --- a/t/t58-daemon-gitweb-wild +++ b/t/t58-daemon-gitweb-wild @@ -78,7 +78,7 @@ do runremote cat repositories/bar/u1/try1.git/description expect "this is bar/u1/try1" runremote cat repositories/bar/u1/try3.git/description - expect "cat: repositories/bar/u1/try3.git/description: No such file or directory" + expect "Unnamed repository; edit this file 'description' to name the repository." name "now compile and recheck try3 stuff" echo " diff --git a/t/t60-daemon-gitweb-via-setperms b/t/t60-daemon-gitweb-via-setperms new file mode 100644 index 0000000..47bc5b7 --- /dev/null +++ b/t/t60-daemon-gitweb-via-setperms @@ -0,0 +1,128 @@ +# vim: syn=sh: +for bc in 0 1 +do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS 1 + editrc GL_BIG_CONFIG $bc + + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + @gbar = bar/CREATOR/..* + repo @gbar + C = @leads + RW+ = @leads + RW = WRITERS @devs + R = READERS + " | ugc + name "nothing set yet" + expect_push_ok "master -> master" + + runlocal git ls-remote u1:bar/u1/try1 + runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try1.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try1.git" + + runlocal git ls-remote u1:bar/u1/try2 + runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try2.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try2.git" + + name "add daemon access to try1" + echo R daemon | runlocal ssh u1 setperms bar/u1/try1 + expect "R daemon" + + runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try1.git/git-daemon-export-ok" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try1.git" + + name "add gitweb access to try2" + echo R gitweb | runlocal ssh u1 setperms bar/u1/try2 + expect "R gitweb" + + runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try2.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + expect "bar/u1/try2.git" + + echo "\$GL_WILDREPOS_DEFPERMS = 'R daemon';" | addrc + + name "add default daemon access" + runlocal git ls-remote u1:bar/u1/try3 + runremote ls -al repositories/bar/u1/try3.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try3.git/git-daemon-export-ok" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try3.git" + + name "add default gitweb access" + echo "\$GL_WILDREPOS_DEFPERMS = 'R gitweb';" | addrc + + runlocal git ls-remote u1:bar/u1/try4 + runremote ls -al repositories/bar/u1/try4.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try4.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 44 .* projects.list" + runremote cat projects.list + expect "bar/u1/try4.git" + + name "add default both access" + echo "\$GL_WILDREPOS_DEFPERMS = 'R daemon gitweb';" | addrc + + runlocal git ls-remote u1:bar/u1/try5 + runremote ls -al repositories/bar/u1/try5.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try5.git/git-daemon-export-ok" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 60 .* projects.list" + runremote cat projects.list + expect "bar/u1/try5.git" + + name "add default both access with @all also" + echo "\$GL_WILDREPOS_DEFPERMS = 'R @all daemon gitweb';" | addrc + + runlocal git ls-remote u1:bar/u1/try6 + runremote ls -al repositories/bar/u1/try6.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try6.git/git-daemon-export-ok" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 76 .* projects.list" + runremote cat projects.list + expect "bar/u1/try6.git" + + name "remove all from u6" + < /dev/null runlocal ssh u1 setperms bar/u1/try6 + + runlocal git ls-remote u1:bar/u1/try6 + runremote ls -al repositories/bar/u1/try6.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try6.git/git-daemon-export-ok: No such file or directory" + # projects.list currently does not get cleared until a compile, so you + # will still see the entry for .../try6 + runremote ls -al projects.list + expect "gitolite-test gitolite-test 76 .* projects.list" + runremote cat projects.list + expect "bar/u1/try6.git" + + echo | ugc + runremote ls -al projects.list + expect "gitolite-test gitolite-test 60 .* projects.list" + runremote cat projects.list + # but *now* it should be gone + notexpect "bar/u1/try6.git" + + name "INTERNAL" + +done From 870983086c4a0c92e2957637ec765156f023607d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 19 Aug 2010 21:48:04 +0530 Subject: [PATCH 447/850] CHANGELOG and progit article updates --- doc/CHANGELOG | 32 ++++++++++++++++++++++++++++++++ doc/progit-article.mkd | 38 ++++++++++++++++++++++---------------- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 978e03f..b870cf8 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,6 +2,38 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] + - more than one wildcard may match a repo, plus it can also be matched by a + normal repo line + + - test suite has lots of new tests for the below + - (big change) all combinations of wild repos and big configs, including + daemon/gitweb/git-config settings, should work now! + + - v1.5.5 + + - mirroring support + + - setup_authkeys is now separate; can be called from outside also; useful + for people who want to maintain ssh keys via LDAP or something, and not + within gitolite + + - (two months too late for towel day) gl-dont-panic! + [replaces the old "gl-emergency-addkey" program. It does more (including + recovering from a botched push, not just lost keys), is cleaner, and works + for all install methods] + + - document on how to create a mob branch + + - info command now takes a parameter to limit output; this is mandatory if + GL_BIG_CONFIG is on + + - v1.5.4 + + - new RC variables: GL_NO_CREATE_REPOS and GL_NO_SETUP_AUTHKEYS (inspired by + the specific needs that Fedora have, but made as generic as possible) + - separating push branch rights from create branch rights changed to use the + same mechanism as the (older) mechanism for separating rewind from delete + - v1.5.3 - log file format changed; minor backward compat breakage if you've been diff --git a/doc/progit-article.mkd b/doc/progit-article.mkd index 8d7fa4f..48bccbb 100644 --- a/doc/progit-article.mkd +++ b/doc/progit-article.mkd @@ -1,6 +1,6 @@ ## Gitolite ## -Git has started to become very popular in corporate environments, which tend to have some additional requirements in terms of access control. Gitolite was created to help with those requirements. +Git has started to become very popular in corporate environments, which tend to have some additional requirements in terms of access control. Gitolite was originally created to help with those requirements, but it turns out that it's equally useful in the open source world: the Fedora Project controls access to their package management repositories (over 10,000 of them!) using gitolite, and this is probably the largest gitolite installation anywhere too. Gitolite allows you to specify permissions not just by repository, but also by branch or tag names within each repository. That is, you can specify that certain people (or groups of people) can only push certain "refs" (branches or tags) but not others. @@ -8,7 +8,13 @@ Gitolite allows you to specify permissions not just by repository, but also by b Installing Gitolite is very easy, even if you don't read the extensive documentation that comes with it. You need an account on a Unix server of some kind; various Linux flavours, and Solaris 10, have been tested. You do not need root access, assuming git, perl, and an openssh compatible ssh server are already installed. In the examples below, we will use the `gitolite` account on a host called `gitserver`. -Curiously, Gitolite is installed by running a script *on the workstation*, so your workstation must have a bash shell available. Even the bash that comes with msysgit will do, in case you're wondering. +Gitolite is somewhat unusual as far as "server" software goes -- access is via ssh, and so every userid on the server is a potential "gitolite host". As a result, there is a notion of "installing" the software itself, and then "setting up" a user as a "gitolite host". + +Gitolite has 4 methods of installation. People using Fedora or Debian systems can obtain an RPM or a DEB and install that. People with root access can install it manually. In these two methods, any user on the system can then become a "gitolite host". + +People without root access can install it within their own userids. And finally, gitolite can be installed by running a script *on the workstation*, from a bash shell. (Even the bash that comes with msysgit will do, in case you're wondering.) + +We will describe this last method in this article; for the other methods please see the documentation. You start by obtaining public key based access to your server, so that you can log in from your workstation to the server without getting a password prompt. The following method works on Linux; for other workstation OSs you may have to do this manually. We assume you already had a key pair generated using `ssh-keygen`. @@ -27,8 +33,6 @@ Next, you clone Gitolite from the project's main site and run the "easy install" And you're done! Gitolite has now been installed on the server, and you now have a brand new repository called `gitolite-admin` in the home directory of your workstation. You administer your gitolite setup by making changes to this repository and pushing. -[By the way, *upgrading* gitolite is also done the same way. Also, if you're interested, run the script without any arguments to get a usage message.] - That last command does produce a fair amount of output, which might be interesting to read. Also, the first time you run this, a new keypair is created; you will have to choose a passphrase or hit enter for none. Why a second keypair is needed, and how it is used, is explained in the "ssh troubleshooting" document that comes with Gitolite. (Hey the documentation has to be good for *something*!) ### Customising the Install ### @@ -37,7 +41,7 @@ While the default, quick, install works for most people, there are some ways to ### Config File and Access Control Rules ### -So once the install is done, you switch to the `gitolite-admin` repository (placed in your HOME directory) and poke around to see what you got: +Once the install is done, you switch to the `gitolite-admin` repository (placed in your HOME directory) and poke around to see what you got: $ cd ~/gitolite-admin/ $ ls @@ -135,24 +139,26 @@ Gitolite allows you to specify repositories with wildcards (actually perl regexe ### Other Features ### -We'll round off this discussion with a bunch of other features, all of which are described in great detail in the "faqs, tips, etc" document. +We'll round off this discussion with a sampling of other features, all of which, and many more, are described in great detail in the "faqs, tips, etc" and other documents. **Logging**: Gitolite logs all successful accesses. If you were somewhat relaxed about giving people rewind permissions (`RW+`) and some kid blew away "master", the log file is a life saver, in terms of easily and quickly finding the SHA that got hosed. **Git outside normal PATH**: One extremely useful convenience feature in gitolite is support for git installed outside the normal `$PATH` (this is more common than you think; some corporate environments or even some hosting providers refuse to install things system-wide and you end up putting them in your own directories). Normally, you are forced to make the *client-side* git aware of this non-standard location of the git binaries in some way. With gitolite, just choose a verbose install and set `$GIT_PATH` in the "rc" files. No client-side changes are required after that :-) -**Access rights reporting**: Another convenient feature is what happens when you try and just ssh to the server. Older versions of gitolite used to complain about the `SSH_ORIGINAL_COMMAND` environment variable being empty (see the ssh documentation if interested). Now Gitolite comes up with something like this: +**Access rights reporting**: Another convenient feature is what happens when you try and just ssh to the server. Gitolite shows you what repos you have access to, and what that access may be. Here's an example: - hello sitaram, the gitolite version here is v0.90-9-g91e1e9f - you have the following permissions: - R anu-wsd - R entrans - R W git-notes - R W gitolite - R W gitolite-admin - R indic_web_input - R shreelipi_converter + hello sitaram, the gitolite version here is v1.5.4-19-ga3397d4 + the gitolite config gives you the following access: + R anu-wsd + R entrans + R W git-notes + R W gitolite + R W gitolite-admin + R indic_web_input + R shreelipi_converter **Delegation**: For really large installations, you can delegate responsibility for groups of repositories to various people and have them manage those pieces independently. This reduces the load on the main admin, and makes him less of a bottleneck. This feature has its own documentation file in the `doc/` directory. **Gitweb support**: Gitolite supports gitweb in several ways. You can specify which repos are visible via gitweb. You can set the "owner" and "description" for gitweb from the gitolite config file. Gitweb has a mechanism for you to implement access control based on HTTP authentication, so you can make it use the "compiled" config file that gitolite produces, which means the same access control rules (for read access) apply for gitweb and gitolite. + +**Mirroring**: Gitolite can help you maintain multiple mirrors, and switch between them easily if the primary server goes down. From 3a8f32ac9bf47c9f1372e589e5e35124fae06a94 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 20 Aug 2010 22:47:34 +0530 Subject: [PATCH 448/850] minor fixups to the "gitweb/daemon from setperms" code... - fork was not printing a newline after the permissions - "add_del_line" to properly handle that damn projects.list file! --- conf/example.gitolite.rc | 2 +- contrib/adc/fork | 2 +- src/gitolite.pm | 23 ++++++++++++++++++++++- src/gl-auth-command | 2 +- t/t60-daemon-gitweb-via-setperms | 9 --------- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 82a9783..e57f8fc 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -231,7 +231,7 @@ $GL_WILDREPOS = 0; # as desired after repository creation; it is only a default. Note that @all can be # used here but is special; no other groups can be used in user-level permissions. -# $GL_WILDREPOS_DEFPERMS = 'R = @all'; +# $GL_WILDREPOS_DEFPERMS = 'R @all'; # -------------------------------------- # HOOK CHAINING diff --git a/contrib/adc/fork b/contrib/adc/fork index b3cac93..70deb4c 100755 --- a/contrib/adc/fork +++ b/contrib/adc/fork @@ -19,7 +19,7 @@ git clone --bare -l $GL_REPO_BASE_ABS/$from.git $GL_REPO_BASE_ABS/$to.git cd $GL_REPO_BASE_ABS/$to.git echo $GL_USER > gl-creater git config gitweb.owner "$GL_USER" -( cd $HOME;perl -e 'do ".gitolite.rc"; print $GL_WILDREPOS_DEFPERMS' ) | +( cd $HOME;perl -le 'do ".gitolite.rc"; print $GL_WILDREPOS_DEFPERMS' ) | SSH_ORIGINAL_COMMAND="setperms $to" $GL_BINDIR/gl-auth-command $GL_USER cp -R $GL_REPO_BASE_ABS/$from.git/hooks/* $GL_REPO_BASE_ABS/$to.git/hooks diff --git a/src/gitolite.pm b/src/gitolite.pm index 13a761d..e0c2af4 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -66,6 +66,26 @@ sub wrap_print { close($fh); } +sub add_del_line { + my ($line, $file, $flag) = @_; + my $contents; + + local $/ = undef; + my $fh = wrap_open("<", $file); + $contents = <$fh>; + $contents =~ s/\s+$/\n/; + + if ($flag and $contents !~ /^\Q$line\E$/m) { + # add line if it doesn't exist + $contents .= "$line\n"; + wrap_print($file, $contents); + } + if (not $flag and $contents =~ /^\Q$line\E$/m) { + $contents =~ s/^\Q$line\E(\n|$)//m; + wrap_print($file, $contents); + } +} + sub dbg { for my $i (@_) { print STDERR "DBG: $i\n"; @@ -279,7 +299,8 @@ sub get_set_perms # gitweb and daemon setup_daemon_access($repo); - system("echo $repo.git >> $PROJECTS_LIST") if &setup_gitweb_access($repo, '', ''); + # add or delete line (arg1) from file (arg2) depending on arg3 + &add_del_line ("$repo.git", $PROJECTS_LIST, &setup_gitweb_access($repo, '', '')); } } diff --git a/src/gl-auth-command b/src/gl-auth-command index 9870b2d..45e6ced 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -199,7 +199,7 @@ if ($perm =~ /C/) { wrap_print("gl-perms", "$GL_WILDREPOS_DEFPERMS\n") if $GL_WILDREPOS_DEFPERMS; &setup_repo_configs($repo, \%repo_config); &setup_daemon_access($repo); - system("echo $repo.git >> $PROJECTS_LIST") if &setup_gitweb_access($repo, '', ''); + &add_del_line ("$repo.git", $PROJECTS_LIST, &setup_gitweb_access($repo, '', '')); wrap_chdir($ENV{HOME}); } diff --git a/t/t60-daemon-gitweb-via-setperms b/t/t60-daemon-gitweb-via-setperms index 47bc5b7..4d23b10 100644 --- a/t/t60-daemon-gitweb-via-setperms +++ b/t/t60-daemon-gitweb-via-setperms @@ -109,18 +109,9 @@ do runlocal git ls-remote u1:bar/u1/try6 runremote ls -al repositories/bar/u1/try6.git/git-daemon-export-ok expect "ls: cannot access repositories/bar/u1/try6.git/git-daemon-export-ok: No such file or directory" - # projects.list currently does not get cleared until a compile, so you - # will still see the entry for .../try6 - runremote ls -al projects.list - expect "gitolite-test gitolite-test 76 .* projects.list" - runremote cat projects.list - expect "bar/u1/try6.git" - - echo | ugc runremote ls -al projects.list expect "gitolite-test gitolite-test 60 .* projects.list" runremote cat projects.list - # but *now* it should be gone notexpect "bar/u1/try6.git" name "INTERNAL" From 8faba2317724b7e3ce5aae35c3bffb647f47038b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 21 Aug 2010 10:39:43 +0530 Subject: [PATCH 449/850] (minor) make time-style=long-iso explicit in t00 (side-effect of switching one machine from Mandriva to Fedora; nothing to do with gitolite per se...) --- t/t00-initial | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t00-initial b/t/t00-initial index c17ee86..bfc97b4 100644 --- a/t/t00-initial +++ b/t/t00-initial @@ -21,7 +21,7 @@ cd ~/td runlocal git clone u1:aa u1aa expect "Cloning into u1aa..." expect "warning: You appear to have cloned an empty repository" -runlocal ls -ald u1aa +runlocal ls -ald --time-style=long-iso u1aa expect "drwxr-xr-x 3 $USER $USER 4096 201.-..-.. ..:.. u1aa" # ---------- From 34965b1b0335b37f8d928e587127b2f147b615a8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 21 Aug 2010 10:38:37 +0530 Subject: [PATCH 450/850] (tests) added tests for delegating with wildcards --- t/t05-delegation | 11 +++- t/t05b-delegation-wild | 116 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 t/t05b-delegation-wild diff --git a/t/t05-delegation b/t/t05-delegation index ad2c36b..b57cfd8 100644 --- a/t/t05-delegation +++ b/t/t05-delegation @@ -3,9 +3,18 @@ for i in 0 1 do - hl t05-delegation with GL_BIG_CONFIG $i + hl t05a-delegation with GL_BIG_CONFIG $i . ./t05a-delegation $i cd $TESTDIR done + +for i in 0 1 +do + hl t05b-delegation-wild with GL_BIG_CONFIG $i + + . ./t05b-delegation-wild $i + + cd $TESTDIR +done diff --git a/t/t05b-delegation-wild b/t/t05b-delegation-wild new file mode 100644 index 0000000..cd37b92 --- /dev/null +++ b/t/t05b-delegation-wild @@ -0,0 +1,116 @@ +# vim: syn=sh: +cd $TESTDIR +$TESTDIR/rollback || die "rollback failed" +editrc GL_BIG_CONFIG $i +editrc GL_WILDREPOS 1 +# ---------- + +name "INTERNAL" +cd ~/gitolite-admin +mkdir -p conf/fragments +echo " + # group your projects/repos however you want + @u1r = r1[ab] + @u2r = r2[ab] + @u3r = r3[ab] + + # the admin repo access was probably like this to start with: + repo gitolite-admin + RW = u1 u2 u3 + RW+ NAME/ = tester + RW NAME/conf/fragments/u1r = u1 + RW NAME/conf/fragments/u2r = u2 + RW NAME/conf/fragments/u3r = u3 +" | ugc + +echo " + repo r1a r1b + C = @all + RW+ = CREATOR + repo @u1r + RW+ = tester +" > conf/fragments/u1r.conf + +echo " + repo @u2r + C = @all + RW+ = CREATOR + repo @u2r + RW+ = tester +" > conf/fragments/u2r.conf + +echo " + repo @u3r + C = @all + RW+ = CREATOR + repo @u3r + RW+ = tester +" > conf/fragments/u3r.conf + +ugc < /dev/null +runlocal git ls-remote gitolite:r1a +runlocal git ls-remote gitolite:r1b +runlocal git ls-remote gitolite:r2a +runlocal git ls-remote gitolite:r2b +runlocal git ls-remote gitolite:r3a +runlocal git ls-remote gitolite:r3b +runremote ls repositories + +name "u1 push frag u1r" +cd ~/gitolite-admin +echo " + repo @u1r + RW+ = u5 +" > conf/fragments/u1r.conf +ugc u1 < /dev/null +expect "To u1:gitolite-admin" +expect "master -> master" + +name "u2 push main conf fail" +cd ~/gitolite-admin +echo " + repo @u1r + RW+ = u6 +" | ugc u2 +expect "W NAME/conf/gitolite.conf gitolite-admin u2 DENIED by fallthru" +expect "To u2:gitolite-admin" +expect "\[remote rejected\] master -> master (hook declined)" +git reset --hard origin/master &>/dev/null + +name "u2 push frag u1r fail" +cd ~/gitolite-admin +echo " + repo @u1r + RW+ = u6 +" > conf/fragments/u1r.conf +ugc u2 < /dev/null +expect "remote: W NAME/conf/fragments/u1r.conf gitolite-admin u2 DENIED by fallthru" +expect "To u2:gitolite-admin" +expect "\[remote rejected\] master -> master (hook declined)" +git reset --hard origin/master &>/dev/null + +name "u3 set perms for r2a fail" +cd ~/gitolite-admin +echo " + repo r2a + RW+ = u6 +" > conf/fragments/u3r.conf +ugc u3 < /dev/null +expect "u3r.conf attempting to set access for r2a" +git reset --hard origin/master &>/dev/null + +name "u3 add r2b to u3r fail" +cd ~/gitolite-admin +echo " + @u3r = r2b + repo @u3r + RW+ = u6 +" > conf/fragments/u3r.conf +ugc u3 < /dev/null +[[ $1 == 0 ]] && expect "u3r.conf attempting to set access for r2b" +[[ $1 == 1 ]] && expect "defining groups is not allowed inside fragments" +[[ $1 == 1 ]] && notexpect "u3r.conf attempting to set access for r2b" +[[ $1 == 0 ]] && notexpect "defining groups is not allowed inside fragments" +git reset --hard origin/master &>/dev/null + +name INTERNAL From 6e2db1230211697091d724cf2483384e9e3a2234 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 21 Aug 2010 09:56:11 +0530 Subject: [PATCH 451/850] allow @groups in setperms command also This should hopefully be the final step in making wildrepos as close to normal repos as possible. You can now do pretty much anything with them that you can do with normal repos [1] Implementation notes: - compile puts out %groups into the compiled config file regardless of GL_BIG_CONFIG because this feature needs it - wild_repo_rights caches %groups because the part of the %groups hash we care about will not change between calls in the same run ---- [1] **except** use the full-blown config file syntax within the gl-perms file :-) I don't plan to do that; it's too complicated! [2] [2] yeah yeah I know -- famous last words! --- src/gitolite.pm | 65 ++++++++++++++++-------- src/gl-compile-conf | 2 +- t/out/t01-repo-groups.1b | 105 +++++++++++++++++++++++++++++++++++++++ t/out/t02-user-groups.1b | 80 +++++++++++++++++++++++++++++ t/t01-repo-groups | 2 +- t/t02-user-groups | 2 +- t/t61-setperms-wild | 72 +++++++++++++++++++++++++++ 7 files changed, 305 insertions(+), 23 deletions(-) create mode 100644 t/out/t01-repo-groups.1b create mode 100644 t/out/t02-user-groups.1b create mode 100644 t/t61-setperms-wild diff --git a/src/gitolite.pm b/src/gitolite.pm index e0c2af4..e78c882 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -255,28 +255,53 @@ sub new_repo # metaphysics (like, "is there a god?", "who created me?", etc) # ---------------------------------------------------------------------------- -# "who created this repo", "am I on the R list", and "am I on the RW list"? -sub wild_repo_rights { - my ($repo, $user) = @_; - # creator - my $c = ''; - if ( -f "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-creater") { - my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-creater"); - chomp($c = <$fh>); - } - # $user's R and W rights - my ($r, $w); $r = ''; $w = ''; - if ($user and -f "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms") { - my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms"); - my $perms = join ("", <$fh>); - if ($perms) { - $r = $user if $perms =~ /^\s*R(?=\s).*\s(\@all|$user)(\s|$)/m; - $w = $user if $perms =~ /^\s*RW(?=\s).*\s(\@all|$user)(\s|$)/m; - } - } + # the following sub needs some persistent data, so we make a closure + my $cache_filled = 0; + my %cached_groups; - return ($c, $r, $w); + # "who created this repo", "am I on the R list", and "am I on the RW list"? + sub wild_repo_rights + { + my ($repo, $user) = @_; + # pull in basic group info + unless ($cache_filled) { + local(%repos, %groups); + # read group info from compiled config. At the time we're called + # this info has not yet been pulled in by the rest of the code, so + # we need to do this specially here. However, the info we're + # looking for is not subject to variable substitutions so we don't + # really care; we just pull it in once and save it for the rest of + # the run + do $GL_CONF_COMPILED; + %cached_groups = %groups; + $cache_filled++; + } + # creator + my $c = ''; + if ( -f "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-creater") { + my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-creater"); + chomp($c = <$fh>); + } + # $user's R and W rights + my ($r, $w); $r = ''; $w = ''; + if ($user and -f "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms") { + my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms"); + my $perms = join ("", <$fh>); + # $perms is say "R alice @foo @bar\nRW bob @baz" (the entire gl-perms + # file). We replace each @foo with $user if $cached_groups{'@foo'}{$user} + # exists (i.e., $user is a member of @foo) + for my $g ($perms =~ /\s(\@\S+)/g) { + $perms =~ s/ $g(?!\S)/ $user/ if $cached_groups{$g}{$user}; + } + if ($perms) { + $r = $user if $perms =~ /^\s*R(?=\s).*\s(\@all|$user)(\s|$)/m; + $w = $user if $perms =~ /^\s*RW(?=\s).*\s(\@all|$user)(\s|$)/m; + } + } + + return ($c, $r, $w); + } } # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index a128dc8..0ccd7d5 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -393,7 +393,7 @@ $dumped_data .= Data::Dumper->Dump([\%repo_config], [qw(*repo_config)]) if %repo # much... $dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g; print $compiled_fh $dumped_data; -if ($GL_BIG_CONFIG and %groups) { +if (%groups) { $dumped_data = Data::Dumper->Dump([\%groups], [qw(*groups)]); $dumped_data =~ s/\bCREAT[EO]R\b/\$creator/g; $dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g; diff --git a/t/out/t01-repo-groups.1b b/t/out/t01-repo-groups.1b new file mode 100644 index 0000000..dac62f2 --- /dev/null +++ b/t/out/t01-repo-groups.1b @@ -0,0 +1,105 @@ +$data_version = '1.5'; +%repos = ( + 'aa' => { + 'R' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'W' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'u1' => [ + [ + 2, + 'refs/.*', + 'RW+' + ] + ], + 'u2' => [ + [ + 4, + 'refs/.*', + 'RW' + ] + ], + 'u3' => [ + [ + 5, + 'refs/.*', + 'RW' + ] + ] + }, + 'bb' => { + 'R' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'W' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'u1' => [ + [ + 3, + 'refs/.*', + 'RW+' + ] + ], + 'u2' => [ + [ + 6, + 'refs/.*', + 'RW' + ] + ], + 'u3' => [ + [ + 7, + 'refs/.*', + 'RW' + ] + ] + }, + 'gitolite-admin' => { + 'R' => { + 'tester' => 1 + }, + 'W' => { + 'tester' => 1 + }, + 'tester' => [ + [ + 0, + 'refs/.*', + 'RW+' + ] + ] + }, + 'testing' => { + '@all' => [ + [ + 1, + 'refs/.*', + 'RW+' + ] + ], + 'R' => { + '@all' => 1 + }, + 'W' => { + '@all' => 1 + } + } +); +%groups = ( + '@g1' => { + 'aa' => 'master', + 'bb' => 'master' + } +); diff --git a/t/out/t02-user-groups.1b b/t/out/t02-user-groups.1b new file mode 100644 index 0000000..cfae71a --- /dev/null +++ b/t/out/t02-user-groups.1b @@ -0,0 +1,80 @@ +$data_version = '1.5'; +%repos = ( + 'aa' => { + 'R' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'W' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'u1' => [ + [ + 2, + 'refs/.*', + 'RW+' + ] + ], + 'u2' => [ + [ + 3, + 'refs/.*', + 'RW' + ] + ], + 'u3' => [ + [ + 4, + 'refs/.*', + 'RW' + ] + ] + }, + 'gitolite-admin' => { + 'R' => { + 'tester' => 1 + }, + 'W' => { + 'tester' => 1 + }, + 'tester' => [ + [ + 0, + 'refs/.*', + 'RW+' + ] + ] + }, + 'testing' => { + '@all' => [ + [ + 1, + 'refs/.*', + 'RW+' + ] + ], + 'R' => { + '@all' => 1 + }, + 'W' => { + '@all' => 1 + } + } +); +%groups = ( + '@g1' => { + 'u1' => 'master' + }, + '@g2' => { + 'u2' => 'master', + 'u3' => 'master' + }, + '@g3' => { + 'u4' => 'master', + 'u5' => 'master', + 'u6' => 'master' + } +); diff --git a/t/t01-repo-groups b/t/t01-repo-groups index d738def..367e29d 100644 --- a/t/t01-repo-groups +++ b/t/t01-repo-groups @@ -26,7 +26,7 @@ echo " " | ugc catconf -expect_filesame $TESTDIR/out/t01-repo-groups.1 +expect_filesame $TESTDIR/out/t01-repo-groups.1b # ---------- $TESTDIR/rollback || die "rollback failed" diff --git a/t/t02-user-groups b/t/t02-user-groups index bc17e17..7b673cc 100644 --- a/t/t02-user-groups +++ b/t/t02-user-groups @@ -29,7 +29,7 @@ echo " " | ugc catconf -expect_filesame $TESTDIR/out/t02-user-groups.1 +expect_filesame $TESTDIR/out/t02-user-groups.1b # ---------- $TESTDIR/rollback || die "rollback failed" diff --git a/t/t61-setperms-wild b/t/t61-setperms-wild new file mode 100644 index 0000000..aa4d8c7 --- /dev/null +++ b/t/t61-setperms-wild @@ -0,0 +1,72 @@ +# vim: syn=sh: +for bc in 0 1 +do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS 1 + editrc GL_BIG_CONFIG $bc + + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + @gbar = bar/CREATOR/..* + repo @gbar + C = @leads + RW+ = CREATOR + RW = WRITERS + R = READERS + " | ugc + name "nothing set yet" + expect_push_ok "master -> master" + + name "u1 auto-creates a repo" + runlocal git ls-remote u1:bar/u1/try1 + expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/u1/try1.git/" + name "default permissions for u2 and u4" + runlocal ssh u1 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u2 expand + notexpect R.*W.*u1.*bar/u1/try1 + runlocal ssh u4 expand + notexpect R.*W.*u1.*bar/u1/try1 + + name "@leads can RW try1" + echo RW @leads | runlocal ssh u1 setperms bar/u1/try1 + expect "RW @leads" + runlocal ssh u1 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u2 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u4 expand + notexpect R.*W.*u1.*bar/u1/try1 + + name "@devs can R try1" + echo R @devs | runlocal ssh u1 setperms bar/u1/try1 + expect "R @devs" + notexpect "RW @leads" + runlocal ssh u1 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u2 expand + notexpect R.*W.*u1.*bar/u1/try1 + expect R.*u1.*bar/u1/try1 + runlocal ssh u4 expand + notexpect R.*W.*u1.*bar/u1/try1 + expect R.*u1.*bar/u1/try1 + + name "combo of previous 2" + printf "R @devs\nRW @leads\n" | runlocal ssh u1 setperms bar/u1/try1 + expect "R @devs" + expect "RW @leads" + runlocal ssh u1 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u2 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u4 expand + notexpect R.*W.*u1.*bar/u1/try1 + expect R.*u1.*bar/u1/try1 + + name "INTERNAL" + +done From d6704d052a8bea31798eeedde5e37d95b5fd3b81 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 21 Aug 2010 17:02:12 +0530 Subject: [PATCH 452/850] doc cleanup on info and expand command jefferai pointed out that some of the links about this were broken, and a quick look showed that it was described in multiple places too. Brought it all together... --- doc/3-faq-tips-etc.mkd | 50 ++------------------------ doc/4-wildcard-repositories.mkd | 62 +++++++++++---------------------- doc/6-ssh-troubleshooting.mkd | 21 ++++++----- doc/report-output.mkd | 58 ++++++++++++++++++++++++++---- 4 files changed, 86 insertions(+), 105 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 4b0a473..4a65e2e 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -438,54 +438,10 @@ Sometimes there are too many repos, maybe even named similarly, or with the potential for typos, confusion about hyphens/underscores or upper/lower case, etc. You'd just like a simple way to know what repos you have access to. -Easy! Just use ssh to give the "info" command to the gitolite server: +Gitolite provides two commands (`info` and `expand`) to help you find this +information; please check [doc/report-output.mkd][repout] for details. - $ ssh git@server info - hello sitaram, the gitolite version here is v1.4.2-4-g40cbecd - the gitolite config gives you the following access: - #R W SecureBrowse - #R W anu-wsd - #R W entrans - @R W git-notes - @R W gitolite - #R W gitolite-admin - #R W indic_web_input - @C #R private/CREATOR/[\w.-]+ - #R W proxy - @C @R W public/CREATOR/[\w.-]+ - @R @W testing - #R W vkc - -To understand what these symbols mean, please see doc/report-output.mkd. - -You can also pass an extra argument to subset the listing; it's treated as a -regex pattern that can match anywhere in the reponame. - -In "big-config" mode (i.e., when `GL_BIG_CONFIG` is set) this argument is -**mandatory**. You can try and cheat the system by passing in a "." but -gitolite truncates the output after 5 results to prevent a DOS. - - $ ssh git@server info git - hello sitaram, the gitolite version here is v1.4.2-4-g40cbecd - the gitolite config gives you the following access: - @R W git-notes - @R W gitolite - #R W gitolite-admin - - $ ssh git@server info admin - hello sitaram, the gitolite version here is v1.4.2-4-g40cbecd - the gitolite config gives you the following access: - #R W gitolite-admin - -The administrator can also say things like: - - # if you installed using the "from-client" method - ssh gitolite info foo u1 u2 u3 - # for the other 3 install methods - ssh git@server info foo u1 u2 u3 - -to get this info for other user(s). The "foo" is a partial reponame or a -regex pattern; see above for details. +[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 5addbfa..759367d 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -20,12 +20,12 @@ workarounds I may not have the time to code it right away. In this document: * rc file setting required - * Wildcard repos - * Wildcard repos with creator name in them - * Wildcard repos without creator name in them - * Side-note: Line-anchored regexes - * Contrast with refexes - * Handing out rights to wildcard-matched repos + * wildcard repos + * wildcard repos with creator name in them + * wildcard repos without creator name in them + * side-note: line-anchored regexes + * contrast with refexes + * handing out rights to wildcard-matched repos * setting a gitweb description for a wildcard-matched repo * reporting * other issues and discussion @@ -45,17 +45,17 @@ This feature requires that you set `$GL_WILDREPOS` to "1" in `~/.gitolite.rc` on the server. Please search for that variable and see comments around it in `conf/example.gitolite.rc` for more information on this. - + -### Wildcard repos +### wildcard repos Which of these alternatives you choose depends on your needs, and the social aspects of your environment. The first one is a little more rigid, making it harder to make mistakes, and the second is more flexible and trusting. - + -#### Wildcard repos with creator name in them +#### wildcard repos with creator name in them Here's an example snippet: @@ -79,9 +79,9 @@ new repo, as user "u4" (a student): Notice the *two* empty repo inits, and the order in which they occur ;-) - + -#### Wildcard repos without creator name in them +#### wildcard repos without creator name in them Here's how the same example would look if you did not want the CREATOR's name to be part of the actual repo name. @@ -106,9 +106,9 @@ and have a TA create the repos in advance. In either case, they could then use the `setperms` feature to specify which users are "READERS" and which are "WRITERS". See later for details. - + -### Side-note: Line-anchored regexes +### side-note: line-anchored regexes A regex like @@ -123,9 +123,9 @@ But you may be surprised to find that it does not match even `^assignments/S[0-9]+/A[0-9]+$` -- notice the line beginning and ending metacharacters. - + -#### Contrast with refexes +#### contrast with refexes Just for interest, note that this is in contrast to the refexes for the normal "branch" permissions, as described in `conf/example.conf` and elsewhere. @@ -135,9 +135,9 @@ if no one will actually push such a branch! You can anchor both sides if you really care, by using `master$` instead of `master`, but that is *not* the default for refexes. - + -### Handing out rights to wildcard-matched repos +### handing out rights to wildcard-matched repos In the examples above, we saw two special "user" names: READERS and WRITERS. The permissions they have are controlled by the config file, but ***who is @@ -193,30 +193,10 @@ commands, thanks to Teemu. ### reporting -Remember the cool stuff you see when you just do `ssh git@server` (grep for -"myrights" in `doc/3-faq-tips-etc.mkd` if you forgot, or go [here][mr]). +In order to see what repositories were created from a wildcard, use the +"expand" command, described briefly in [doc/report-output.mkd][repout]. -[mr]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#myrights - -This still works, except the format is a little more compressed to accommodate -a new column (at the start) for "C" permissions, which indicate that you are -allowed to *create* repos matching that pattern. - -In addition, there is also the "expand" command, which takes any regex pattern -and returns you a list of all wildcard-created repos that you have access to -which fit that pattern. And if, as an administrator, you wish to list out -*every single* repo that your users have created, add this to your config -file: - - repo @all - R = sitaram # or whoever you are - -Push the config, then try - - # if you installed using the "from-client" method - ssh gitolite expand - # for the other 3 install methods - ssh git@server expand +[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index d129aff..36e51d9 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -184,14 +184,14 @@ Otherwise, run these checks: you ran the easy install, or someone fiddled with the `~/.ssh/authorized_keys` file on the server. - If it prints [gitolite version and access info][myrights], you managed to - overwrite the `id_rsa` keypair with the `sitaram` keypair, or something - equally weird. + If it prints the gitolite version and access info (see + [doc/report-output.mkd][repout]), you managed to overwrite the `id_rsa` + keypair with the `sitaram` keypair, or something equally weird. -2. `ssh gitolite info` should print some [gitolite version and access - info][myrights]. If you get the output of the GNU info command instead, - you probably reused your `id_rsa` keypair as your `sitaram` keypair, or - overwrote the `sitaram` keypair with the `id_rsa` keypair. +2. `ssh gitolite info` should print some gitolite version and access info. + If you get the output of the GNU info command instead, you probably reused + your `id_rsa` keypair as your `sitaram` keypair, or overwrote the + `sitaram` keypair with the `id_rsa` keypair. There are many ways to fix this, depending on where and what the damage is. The most generic way (and therefore time-taking) is to re-install gitolite @@ -229,10 +229,8 @@ which was previously sent to the gitolite admin to add into the admin repo's `keydir` as "user.pub", and then "user" given permissions to some repo. `ssh git@server info` should get you [gitolite version and access -info][myrights]. If it asks you for a password, your pubkey was not sent to -the server properly. Check with your admin. - -[myrights]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#myrights +info][repout]. If it asks you for a password, your pubkey was not sent to the +server properly. Check with your admin. If it gets you the GNU info command output, you have shell access. This means you had command line access to the server *before* you were added as a @@ -462,3 +460,4 @@ bigger problems than gitolite install not working!)] [o3]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#installation_and_setup [fc]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#from_client_method_install_from_the_client_to_the_server [urls]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#URLs_for_gitolite_managed_repos +[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd diff --git a/doc/report-output.mkd b/doc/report-output.mkd index ba632e4..7e53907 100644 --- a/doc/report-output.mkd +++ b/doc/report-output.mkd @@ -4,10 +4,13 @@ Running "ssh git@server info" or "ssh git@server expand" gives you certain output. This doclet describes the output; you're welcome to help me make it clearer :) -There are 3 columns of permissions (create, read, and write) in the output, -although the first column is often blank. +(Side note: if you installed using the "from-client" method, and you're the +administrator, please replace `ssh git@server` with `ssh gitolite`, all +through this document). -Here is the output of the 2 commands (info and expand): +---- + + ### the "info" command @@ -20,14 +23,57 @@ file that you have been given any kind of access to. If you supply an optional pattern the output will be limited to repos matching that pattern. If you're an admin you can append a list of users to see their permissions instead of your own; in this mode the pattern is mandatory, even if you just -use `.` to cheat. (Side note: if you installed using easy-install that would -probably be `ssh gitolite info`, by the way). +use `.` to cheat. + +Here is a sample output of the info command. There are 3 columns of +permissions (create, read, and write) in the output, although the first column +is often blank. + + $ ssh git@server info + hello sitaram, the gitolite version here is v1.5.4-19-ga3397d4 + the gitolite config gives you the following access: + #R W SecureBrowse + #R W anu-wsd + #R W entrans + @R W git-notes + @R W gitolite + #R W gitolite-admin + #R W indic_web_input + @C #R private/CREATOR/[\w.-]+ + #R W proxy + @C @R W public/CREATOR/[\w.-]+ + @R @W testing + #R W vkc The meaning of C, R, and W are self-explanatory, but they might sometimes be prefixed by a symbol. For example, `@R` means that `@all` users have -beengiven this access, and `#R` means that this user is a "superuser" (think +been given this access, and `#R` means that this user is a "superuser" (think root's shell prompt) and so has access to `@all` repos. +Here are a couple of samples with optional patterns: + + $ ssh git@server info git + hello sitaram, the gitolite version here is v1.5.4-19-ga3397d4 + the gitolite config gives you the following access: + @R W git-notes + @R W gitolite + #R W gitolite-admin + + $ ssh git@server info admin + hello sitaram, the gitolite version here is v1.5.4-19-ga3397d4 + the gitolite config gives you the following access: + #R W gitolite-admin + +In "big-config" mode (i.e., when `GL_BIG_CONFIG` is set) the pattern is +**mandatory**. You can try and cheat the system by passing in a "." but +gitolite truncates the output after 20 results to prevent a DOS. + +The pattern is also mandatory when an admin wants to find out what access some +*other* user has, which you may have guessed from the syntax in the "usage" +line above. + + + ### the "expand" command Usage: From 2b066fc9f07e4aab228082d3420f718991a63f77 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 24 Aug 2010 13:34:27 +0530 Subject: [PATCH 453/850] info/expand print better(?) permission codes see doc/report-output.mkd for details --- src/gitolite.pm | 21 +++++++++++++++++---- t/t50-sequence-test | 4 ++-- t/t53-check-info-expand-output | 12 ++++++------ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index e78c882..d1f915c 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -518,6 +518,19 @@ sub report_version { system("cat", ($GL_PACKAGE_CONF || "$GL_ADMINDIR/conf") . "/VERSION"); } +sub perm_code { + # print the permission code + my($all, $super, $user, $x) = @_; + return " " unless $all or $super or $user; + return " $x " unless $all or $super; # only $user (explicit access) was given + my $ret; + $ret = " \@$x" if $all; # prefix @ if repo allows access for @all users + $ret = " \#$x" if $super; # prefix # if user has access to @all repos (sort of like a super user) + $ret = " \&$x" if $all and $super; # prefix & if both the above + $ret .= ($user ? " " : "_" ); # suffix _ if no explicit access else + return $ret; +} + # basic means wildcards will be shown as wildcards; this is pretty much what # got parsed by the compile script sub report_basic @@ -551,8 +564,8 @@ sub report_basic # #R => you're a super user and can see @all repos # R => normal access my $perm .= ( $repos{$r}{C}{'@all'} ? ' @C' : ( $repos{$r}{C}{$user} ? ' C' : ' ' ) ); - $perm .= ( $repos{$r}{R}{'@all'} ? ' @R' : ( $repos{'@all'}{R}{$user} ? ' #R' : ( $repos{$r}{R}{$user} ? ' R' : ' ' ))); - $perm .= ( $repos{$r}{W}{'@all'} ? ' @W' : ( $repos{'@all'}{W}{$user} ? ' #W' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ))); + $perm .= &perm_code( $repos{$r}{R}{'@all'}, $repos{'@all'}{R}{$user}, $repos{$r}{R}{$user}, 'R'); + $perm .= &perm_code( $repos{$r}{W}{'@all'}, $repos{'@all'}{W}{$user}, $repos{$r}{W}{$user}, 'W'); print "$perm\t$r\r\n" if $perm =~ /\S/; } print "only 20 out of $count candidate repos examined\r\nplease use a partial reponame or regex pattern to limit output\r\n" if $GL_BIG_CONFIG and $count > 20; @@ -648,8 +661,8 @@ sub expand_wild delete $repos{$repo} if $perm !~ /C/ and $wild; $creator = ""; } - $perm .= ( $repos{$repo}{R}{'@all'} ? ' @R' : ( $repos{'@all'}{R}{$ENV{GL_USER}} ? ' #R' : ( $repos{$repo}{R}{$ENV{GL_USER}} ? ' R' : ' ' ))); - $perm .= ( $repos{$repo}{W}{'@all'} ? ' @W' : ( $repos{'@all'}{W}{$ENV{GL_USER}} ? ' #W' : ( $repos{$repo}{W}{$ENV{GL_USER}} ? ' W' : ' ' ))); + $perm .= &perm_code( $repos{$repo}{R}{'@all'}, $repos{'@all'}{R}{$ENV{GL_USER}}, $repos{$repo}{R}{$ENV{GL_USER}}, 'R' ); + $perm .= &perm_code( $repos{$repo}{W}{'@all'}, $repos{'@all'}{W}{$ENV{GL_USER}}, $repos{$repo}{W}{$ENV{GL_USER}}, 'W' ); # set up for caching %repos $last_repo = $repo; diff --git a/t/t50-sequence-test b/t/t50-sequence-test index f90518d..8326c3c 100644 --- a/t/t50-sequence-test +++ b/t/t50-sequence-test @@ -33,7 +33,7 @@ do expect "RW u2" name "expand" runlocal ssh u2 expand - expect "R W.(u1).foo/u1/bar" + expect "R W .(u1).foo/u1/bar" name "push" cd ~/td @@ -80,7 +80,7 @@ do expect "RW u2" name "expand" runlocal ssh u2 expand - expect "R W.(u1).foo/u1/bar" + expect " R W .(u1).foo/u1/bar" name "push" cd ~/td diff --git a/t/t53-check-info-expand-output b/t/t53-check-info-expand-output index 563d4bd..1d3bc28 100644 --- a/t/t53-check-info-expand-output +++ b/t/t53-check-info-expand-output @@ -36,17 +36,17 @@ do name "u1 info" runlocal ssh u1 info expect "hello u1, the gitolite version here is" - expect "@R.*@W.*testing" - [ "$wr" = "1" ] && [ "$bc" = "0" ] && expect "C.*R.*W.*foo/u1/\\.\\+" - [ "$wr" = "1" ] && [ "$bc" = "1" ] && notexpect "C.*R.*W.*foo/u1/\\.\\+" - [ "$wr" = "1" ] && [ "$bc" = "1" ] && expect "C.*@gfoo" + expect " @R_ @W_.testing" + [ "$wr" = "1" ] && [ "$bc" = "0" ] && expect " C R W .foo/u1/\\.\\+" + [ "$wr" = "1" ] && [ "$bc" = "1" ] && notexpect " C R W .foo/u1/\\.\\+" + [ "$wr" = "1" ] && [ "$bc" = "1" ] && expect " C .@gfoo" name "u1 expand" runlocal ssh u1 expand [ "$wr" = "0" ] && expect "wildrepos disabled, sorry" [ "$wr" = "1" ] && expect "hello u1, the gitolite version here is" - [ "$wr" = "1" ] && expect "R.*W.*(u1).*foo/u1/bar" - [ "$wr" = "1" ] && expect "@R.*@W.*testing" + [ "$wr" = "1" ] && expect " R W .(u1).foo/u1/bar" + [ "$wr" = "1" ] && expect "@R_ @W_..gitolite..testing" name INTERNAL done From d78f66af526e3a46c5b4a00d74796824cbb152ac Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 24 Aug 2010 17:06:53 +0530 Subject: [PATCH 454/850] doc for change in info/expand command outputs --- doc/report-output.mkd | 89 +++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/doc/report-output.mkd b/doc/report-output.mkd index 7e53907..255cab6 100644 --- a/doc/report-output.mkd +++ b/doc/report-output.mkd @@ -8,6 +8,13 @@ clearer :) administrator, please replace `ssh git@server` with `ssh gitolite`, all through this document). +In this document: + + * the "info" command + * interpreting the output + * using patterns to limit output + * the "expand" command + ---- @@ -29,40 +36,64 @@ Here is a sample output of the info command. There are 3 columns of permissions (create, read, and write) in the output, although the first column is often blank. - $ ssh git@server info - hello sitaram, the gitolite version here is v1.5.4-19-ga3397d4 - the gitolite config gives you the following access: - #R W SecureBrowse - #R W anu-wsd - #R W entrans - @R W git-notes - @R W gitolite - #R W gitolite-admin - #R W indic_web_input - @C #R private/CREATOR/[\w.-]+ - #R W proxy - @C @R W public/CREATOR/[\w.-]+ - @R @W testing - #R W vkc + $ ssh git@server info + hello sitaram, the gitolite version here is v1.5.5-24-g2b066fc + the gitolite config gives you the following access: + R W SecureBrowse + R W anu-wsd + R W entrans + @R W git-notes + @R W gitolite + R W gitolite-admin + R W indic_web_input + @C R W private/sitaram/[\w.-]+ + R W proxy + @C @R W public/sitaram/[\w.-]+ + @R_ @W_ testing + R W vkc -The meaning of C, R, and W are self-explanatory, but they might sometimes be -prefixed by a symbol. For example, `@R` means that `@all` users have -been given this access, and `#R` means that this user is a "superuser" (think -root's shell prompt) and so has access to `@all` repos. + + +#### interpreting the output + +The meaning of C, R, and W are self-explanatory, but they may be prefixed or +suffixed by a symbol: + + * an `@` prefix means "@all" users have been given this permission + + repo foo + R = @all + + * a `#` prefix means this user is a "superuser" (think root's shell prompt) + and so has access to `@all` repos. Which means you'll see this prefix + (or, in some cases, an `&`; see next bullet) for *all* the repos, or none + of them + + repo @all + R = sitaram + + * an `&` prefix means both of the above are true + +The `_` suffix is special. This says the user has only implicit access (due +to one of the `@all` uses), but no explicit access. + + + +#### using patterns to limit output Here are a couple of samples with optional patterns: - $ ssh git@server info git - hello sitaram, the gitolite version here is v1.5.4-19-ga3397d4 - the gitolite config gives you the following access: - @R W git-notes - @R W gitolite - #R W gitolite-admin + $ ssh git@server info git + hello sitaram, the gitolite version here is v1.5.5-24-g2b066fc + the gitolite config gives you the following access: + @R W git-notes + @R W gitolite + R W gitolite-admin - $ ssh git@server info admin - hello sitaram, the gitolite version here is v1.5.4-19-ga3397d4 - the gitolite config gives you the following access: - #R W gitolite-admin + $ ssh git@server info admin + hello sitaram, the gitolite version here is v1.5.5-24-g2b066fc + the gitolite config gives you the following access: + R W gitolite-admin In "big-config" mode (i.e., when `GL_BIG_CONFIG` is set) the pattern is **mandatory**. You can try and cheat the system by passing in a "." but From 6b9e75dfcb96770597e3cde8519c900501afde37 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 24 Aug 2010 17:17:41 +0530 Subject: [PATCH 455/850] allow @all to also include gitweb and daemon By default, @all does not include gitweb and daemon, but if that's what you want, you can make it happen... see GL_ALL_INCLUDES_SPECIAL variable in conf/example.gitolite.rc --- conf/example.gitolite.rc | 6 + src/gitolite.pm | 13 +- t/t57-daemon-gitweb | 213 +++++++++++++----------- t/t58-daemon-gitweb-wild | 268 +++++++++++++++++-------------- t/t60-daemon-gitweb-via-setperms | 241 ++++++++++++++++----------- 5 files changed, 421 insertions(+), 320 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index e57f8fc..db88dbd 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -43,6 +43,12 @@ $REPO_UMASK = 0077; # gets you 'rwx------' $PROJECTS_LIST = $ENV{HOME} . "/projects.list"; +# giving access to @all users (as in "R = @all") in the config normally does +# *not* include the special users "gitweb" and "daemon". If you want @all to +# include these two users, set this variable: + +# $GL_ALL_INCLUDES_SPECIAL = 0; + # -------------------------------------- # I see no reason anyone may want to change the gitolite admin directory, but diff --git a/src/gitolite.pm b/src/gitolite.pm index d1f915c..78d166f 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -38,7 +38,7 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple patter our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file -our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST); +our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST, $GL_ALL_INCLUDES_SPECIAL); our %repos; our %groups; our %repo_config; @@ -295,8 +295,10 @@ sub new_repo $perms =~ s/ $g(?!\S)/ $user/ if $cached_groups{$g}{$user}; } if ($perms) { - $r = $user if $perms =~ /^\s*R(?=\s).*\s(\@all|$user)(\s|$)/m; - $w = $user if $perms =~ /^\s*RW(?=\s).*\s(\@all|$user)(\s|$)/m; + $r ='@all' if $perms =~ /^\s*R(?=\s).*\s\@all(\s|$)/m; + $r = $user if $perms =~ /^\s*R(?=\s).*\s$user(\s|$)/m; + $w ='@all' if $perms =~ /^\s*RW(?=\s).*\s\@all(\s|$)/m; + $w = $user if $perms =~ /^\s*RW(?=\s).*\s$user(\s|$)/m; } } @@ -684,7 +686,10 @@ sub can_read { my $user = shift || $ENV{GL_USER}; local $ENV{GL_USER} = $user; my ($perm, $creator, $wild) = &repo_rights($repo); - return $perm =~ /R/; + return ( ($GL_ALL_INCLUDES_SPECIAL || $user !~ /^(gitweb|daemon)$/) + ? $perm =~ /R/ + : $perm =~ /R / + ); } # ---------------------------------------------------------------------------- diff --git a/t/t57-daemon-gitweb b/t/t57-daemon-gitweb index 7b501e3..93397c5 100644 --- a/t/t57-daemon-gitweb +++ b/t/t57-daemon-gitweb @@ -3,113 +3,132 @@ for wr in 0 1 do for bc in 0 1 do - cd $TESTDIR - $TESTDIR/rollback || die "rollback failed" - editrc GL_WILDREPOS $wr - editrc GL_BIG_CONFIG $bc + for ais in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS $wr + editrc GL_BIG_CONFIG $bc + echo "\$GL_ALL_INCLUDES_SPECIAL = $ais;" | addrc - # ---------- + # ---------- + name "INTERNAL" + echo|ugc + name "daemon access for testing repo" + runremote ls -al repositories/testing.git/git-daemon-export-ok + [ "$ais" = "0" ] && expect "ls: cannot access repositories/testing.git/git-daemon-export-ok: No such file or directory" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test .* repositories/testing.git/git-daemon-export-ok" - name "INTERNAL" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - @gbar = bar - repo @gbar - RW+ = @leads - RW = @devs - " | ugc - expect_push_ok "master -> master" - runremote ls -al repositories/bar.git/git-daemon-export-ok - expect "ls: cannot access repositories/bar.git/git-daemon-export-ok: No such file or directory" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 12 .* projects.list" - runremote cat projects.list - expect "testing.git" + @gbar = bar + repo @gbar + RW+ = @leads + RW = @devs + " | ugc + expect_push_ok "master -> master" + runremote ls -al repositories/bar.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 0 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" - name "add daemon access to bar" - echo " - R = daemon - " | ugc - runremote ls -al repositories/bar.git/git-daemon-export-ok - expect "gitolite-test gitolite-test .* repositories/bar.git/git-daemon-export-ok" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 12 .* projects.list" + name "add daemon access to bar" + echo " + R = daemon + " | ugc + runremote ls -al repositories/bar.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 0 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 12 .* projects.list" - name "add foo with gitweb access" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 + name "add foo with gitweb access" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - @gfoo = foo - repo @gfoo - RW+ = @leads - RW = @devs - R = gitweb - " | ugc - expect_push_ok "master -> master" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 20 .* projects.list" - runremote cat projects.list - expect "testing.git" - expect "foo.git" + @gfoo = foo + repo @gfoo + RW+ = @leads + RW = @devs + R = gitweb + " | ugc + expect_push_ok "master -> master" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 8 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 20 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + expect "foo.git" - name "add gitweb access to bar" - echo " - repo @gbar - R = gitweb - " | ugc - expect_push_ok "master -> master" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 28 .* projects.list" - runremote cat projects.list - expect "testing.git" - expect "foo.git" - expect "bar.git" + name "add gitweb access to bar" + echo " + repo @gbar + R = gitweb + " | ugc + expect_push_ok "master -> master" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 16 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + expect "foo.git" + expect "bar.git" - name "add repo baz with description" - echo " - @gbaz = baz - repo @gbaz - RW = @leads + name "add repo baz with description" + echo " + @gbaz = baz + repo @gbaz + RW = @leads - baz = \"this is repo baz\" - " | ugc - expect_push_ok "master -> master" - expect "remote: Initialized empty Git repository in /home/gitolite-test/repositories/baz.git/" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 36 .* projects.list" - runremote cat projects.list - expect "testing.git" - expect "foo.git" - expect "bar.git" - expect "baz.git" - runremote ls -al repositories/baz.git/description - expect "gitolite-test gitolite-test 17 .* repositories/baz.git/description" - runremote cat repositories/baz.git/description - expect "this is repo baz" + baz = \"this is repo baz\" + " | ugc + expect_push_ok "master -> master" + expect "remote: Initialized empty Git repository in /home/gitolite-test/repositories/baz.git/" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 24 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 36 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + expect "foo.git" + expect "bar.git" + expect "baz.git" + runremote ls -al repositories/baz.git/description + expect "gitolite-test gitolite-test 17 .* repositories/baz.git/description" + runremote cat repositories/baz.git/description + expect "this is repo baz" - name "add owner to testing repo" - echo " - testing \"sitaram\" = \"this is the testing repo\" - " | ugc - expect_push_ok "master -> master" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 36 .* projects.list" - runremote cat projects.list - expect "testing.git" - expect "foo.git" - expect "bar.git" - expect "baz.git" - runremote cat repositories/testing.git/config - expect "\[gitweb\]" - expect "owner = sitaram" - runremote ls -al repositories/testing.git/description - expect "gitolite-test gitolite-test 25 .* repositories/testing.git/description" - runremote cat repositories/testing.git/description - expect "this is the testing repo" + name "add owner to testing repo" + echo " + testing \"sitaram\" = \"this is the testing repo\" + " | ugc + expect_push_ok "master -> master" + runremote ls -al projects.list + expect "gitolite-test gitolite-test 36 .* projects.list" + runremote cat projects.list + expect "testing.git" + expect "foo.git" + expect "bar.git" + expect "baz.git" + runremote cat repositories/testing.git/config + expect "\[gitweb\]" + expect "owner = sitaram" + runremote ls -al repositories/testing.git/description + expect "gitolite-test gitolite-test 25 .* repositories/testing.git/description" + runremote cat repositories/testing.git/description + expect "this is the testing repo" - name INTERNAL + name INTERNAL + done done done diff --git a/t/t58-daemon-gitweb-wild b/t/t58-daemon-gitweb-wild index 67e2ba5..c22a52c 100644 --- a/t/t58-daemon-gitweb-wild +++ b/t/t58-daemon-gitweb-wild @@ -1,140 +1,160 @@ # vim: syn=sh: for bc in 0 1 do - cd $TESTDIR - $TESTDIR/rollback || die "rollback failed" - editrc GL_WILDREPOS 1 - editrc GL_BIG_CONFIG $bc + for ais in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS 1 + editrc GL_BIG_CONFIG $bc + echo "\$GL_ALL_INCLUDES_SPECIAL = $ais;" | addrc - # ---------- + # ---------- - name "INTERNAL" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - @gbar = bar/CREATOR/..* - repo @gbar - C = @leads - RW+ = @leads - RW = @devs - " | ugc - expect_push_ok "master -> master" - runlocal git ls-remote u1:bar/u1/try1 - runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok - expect "ls: cannot access repositories/bar/u1/try1.git/git-daemon-export-ok: No such file or directory" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 12 .* projects.list" - runremote cat projects.list - expect "testing.git" - notexpect "bar/u1/try1.git" + @gbar = bar/CREATOR/..* + repo @gbar + C = @leads + RW+ = @leads + RW = @devs + " | ugc + expect_push_ok "master -> master" + runlocal git ls-remote u1:bar/u1/try1 + runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try1.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 0 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + notexpect "bar/u1/try1.git" - name "add daemon access" - echo " - R = daemon - " | ugc - runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok - expect "gitolite-test gitolite-test .* repositories/bar/u1/try1.git/git-daemon-export-ok" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 12 .* projects.list" + name "add daemon access" + echo " + R = daemon + " | ugc + runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try1.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 0 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 12 .* projects.list" - name "add one more repo" - runlocal git ls-remote u1:bar/u1/try2 - runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok - expect "gitolite-test gitolite-test .* repositories/bar/u1/try2.git/git-daemon-export-ok" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 12 .* projects.list" - runremote cat projects.list - expect "testing.git" - notexpect "bar/u1/try1.git" - notexpect "bar/u1/try2.git" + name "add one more repo" + runlocal git ls-remote u1:bar/u1/try2 + runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try2.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 0 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + notexpect "bar/u1/try1.git" + notexpect "bar/u1/try2.git" - name "add descriptions for try1 and try3 and compile" - echo " - bar/u1/try1 = \"this is bar/u1/try1\" - bar/u1/try3 = \"this is bar/u1/try3\" - " | ugc - runremote ls -al projects.list - expect "gitolite-test gitolite-test 28 .* projects.list" - runremote cat projects.list - expect "testing.git" - expect "bar/u1/try1.git" - notexpect "bar/u1/try2.git" - notexpect "bar/u1/try3.git" - runremote cat repositories/bar/u1/try1.git/description - expect "this is bar/u1/try1" + name "add descriptions for try1 and try3 and compile" + echo " + bar/u1/try1 = \"this is bar/u1/try1\" + bar/u1/try3 = \"this is bar/u1/try3\" + " | ugc + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 16 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + expect "bar/u1/try1.git" + notexpect "bar/u1/try2.git" + notexpect "bar/u1/try3.git" + runremote cat repositories/bar/u1/try1.git/description + expect "this is bar/u1/try1" - name "add try3 project" - runlocal git ls-remote u1:bar/u1/try3 - runremote ls -al repositories/bar/u1/try3.git/git-daemon-export-ok - expect "gitolite-test gitolite-test .* repositories/bar/u1/try3.git/git-daemon-export-ok" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 28 .* projects.list" - runremote cat projects.list - expect "testing.git" - expect "bar/u1/try1.git" - notexpect "bar/u1/try2.git" - notexpect "bar/u1/try3.git" - runremote cat repositories/bar/u1/try1.git/description - expect "this is bar/u1/try1" - runremote cat repositories/bar/u1/try3.git/description - expect "Unnamed repository; edit this file 'description' to name the repository." + name "add try3 project" + runlocal git ls-remote u1:bar/u1/try3 + runremote ls -al repositories/bar/u1/try3.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try3.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 16 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + expect "bar/u1/try1.git" + notexpect "bar/u1/try2.git" + notexpect "bar/u1/try3.git" + runremote cat repositories/bar/u1/try1.git/description + expect "this is bar/u1/try1" + runremote cat repositories/bar/u1/try3.git/description + expect "Unnamed repository; edit this file 'description' to name the repository." - name "now compile and recheck try3 stuff" - echo " - " | ugc - runremote ls -al repositories/bar/u1/try3.git/git-daemon-export-ok - expect "gitolite-test gitolite-test .* repositories/bar/u1/try3.git/git-daemon-export-ok" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 44 .* projects.list" - runremote cat projects.list - expect "testing.git" - expect "bar/u1/try1.git" - notexpect "bar/u1/try2.git" - expect "bar/u1/try3.git" - runremote cat repositories/bar/u1/try1.git/description - expect "this is bar/u1/try1" - runremote cat repositories/bar/u1/try3.git/description - expect "this is bar/u1/try3" + name "now compile and recheck try3 stuff" + echo " + " | ugc + runremote ls -al repositories/bar/u1/try3.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try3.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 32 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 44 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + expect "bar/u1/try1.git" + notexpect "bar/u1/try2.git" + expect "bar/u1/try3.git" + runremote cat repositories/bar/u1/try1.git/description + expect "this is bar/u1/try1" + runremote cat repositories/bar/u1/try3.git/description + expect "this is bar/u1/try3" - name "add owner for try2 and compile" - echo " - bar/u1/try2 \"owner2\" = \"this is bar/u1/try1\" - " | ugc - runremote cat repositories/bar/u1/try2.git/config - expect "\[gitweb\]" - expect "owner = owner2" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 60 .* projects.list" - runremote cat projects.list - expect "bar/u1/try2.git" + name "add owner for try2 and compile" + echo " + bar/u1/try2 \"owner2\" = \"this is bar/u1/try1\" + " | ugc + runremote cat repositories/bar/u1/try2.git/config + expect "\[gitweb\]" + expect "owner = owner2" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 48 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 60 .* projects.list" + runremote cat projects.list + expect "bar/u1/try2.git" - name "add gitweb access to all" - echo " - repo @gbar - R = gitweb - " | ugc - expect_push_ok "master -> master" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 60 .* projects.list" - runremote cat projects.list - expect "testing.git" - expect "bar/u1/try1.git" - expect "bar/u1/try2.git" - expect "bar/u1/try3.git" + name "add gitweb access to all" + echo " + repo @gbar + R = gitweb + " | ugc + expect_push_ok "master -> master" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 48 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 60 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + expect "bar/u1/try1.git" + expect "bar/u1/try2.git" + expect "bar/u1/try3.git" - name "add try4 project" - runlocal git ls-remote u1:bar/u1/try4 - runremote ls -al projects.list - expect "gitolite-test gitolite-test 76 .* projects.list" - runremote cat projects.list - expect "testing.git" - expect "bar/u1/try1.git" - expect "bar/u1/try2.git" - expect "bar/u1/try3.git" - expect "bar/u1/try4.git" - runremote cat repositories/bar/u1/try4.git/description - expect "Unnamed repository; edit this file 'description' to name the repository." + name "add try4 project" + runlocal git ls-remote u1:bar/u1/try4 + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 64 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 76 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "testing.git" + [ "$ais" = "1" ] && expect "testing.git" + expect "bar/u1/try1.git" + expect "bar/u1/try2.git" + expect "bar/u1/try3.git" + expect "bar/u1/try4.git" + runremote cat repositories/bar/u1/try4.git/description + expect "Unnamed repository; edit this file 'description' to name the repository." - name "INTERNAL" + name "INTERNAL" + done done diff --git a/t/t60-daemon-gitweb-via-setperms b/t/t60-daemon-gitweb-via-setperms index 4d23b10..c98d59b 100644 --- a/t/t60-daemon-gitweb-via-setperms +++ b/t/t60-daemon-gitweb-via-setperms @@ -1,119 +1,170 @@ # vim: syn=sh: for bc in 0 1 do - cd $TESTDIR - $TESTDIR/rollback || die "rollback failed" - editrc GL_WILDREPOS 1 - editrc GL_BIG_CONFIG $bc + for ais in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS 1 + editrc GL_BIG_CONFIG $bc + echo "\$GL_ALL_INCLUDES_SPECIAL = $ais;" | addrc - name "INTERNAL" - echo " - @leads = u1 u2 - @devs = u1 u2 u3 u4 + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 - @gbar = bar/CREATOR/..* - repo @gbar - C = @leads - RW+ = @leads - RW = WRITERS @devs - R = READERS - " | ugc - name "nothing set yet" - expect_push_ok "master -> master" + @gbar = bar/CREATOR/..* + repo @gbar + C = @leads + RW+ = @leads + RW = WRITERS @devs + R = READERS + " | ugc + name "nothing set yet" + expect_push_ok "master -> master" - runlocal git ls-remote u1:bar/u1/try1 - runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok - expect "ls: cannot access repositories/bar/u1/try1.git/git-daemon-export-ok: No such file or directory" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 12 .* projects.list" - runremote cat projects.list - notexpect "bar/u1/try1.git" + runlocal git ls-remote u1:bar/u1/try1 + runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try1.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 0 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try1.git" - runlocal git ls-remote u1:bar/u1/try2 - runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok - expect "ls: cannot access repositories/bar/u1/try2.git/git-daemon-export-ok: No such file or directory" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 12 .* projects.list" - runremote cat projects.list - notexpect "bar/u1/try2.git" + runlocal git ls-remote u1:bar/u1/try2 + runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try2.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 0 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try2.git" - name "add daemon access to try1" - echo R daemon | runlocal ssh u1 setperms bar/u1/try1 - expect "R daemon" + name "add daemon access to try1" + echo R daemon | runlocal ssh u1 setperms bar/u1/try1 + expect "R daemon" - runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok - expect "gitolite-test gitolite-test .* repositories/bar/u1/try1.git/git-daemon-export-ok" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 12 .* projects.list" - runremote cat projects.list - notexpect "bar/u1/try1.git" + runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try1.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 0 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 12 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try1.git" - name "add gitweb access to try2" - echo R gitweb | runlocal ssh u1 setperms bar/u1/try2 - expect "R gitweb" + name "add gitweb access to try2" + echo R gitweb | runlocal ssh u1 setperms bar/u1/try2 + expect "R gitweb" - runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok - expect "ls: cannot access repositories/bar/u1/try2.git/git-daemon-export-ok: No such file or directory" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 28 .* projects.list" - runremote cat projects.list - expect "bar/u1/try2.git" + runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try2.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 16 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + expect "bar/u1/try2.git" - echo "\$GL_WILDREPOS_DEFPERMS = 'R daemon';" | addrc + echo "\$GL_WILDREPOS_DEFPERMS = 'R daemon';" | addrc - name "add default daemon access" - runlocal git ls-remote u1:bar/u1/try3 - runremote ls -al repositories/bar/u1/try3.git/git-daemon-export-ok - expect "gitolite-test gitolite-test .* repositories/bar/u1/try3.git/git-daemon-export-ok" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 28 .* projects.list" - runremote cat projects.list - notexpect "bar/u1/try3.git" + name "add default daemon access" + runlocal git ls-remote u1:bar/u1/try3 + runremote ls -al repositories/bar/u1/try3.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try3.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 16 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 28 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try3.git" - name "add default gitweb access" - echo "\$GL_WILDREPOS_DEFPERMS = 'R gitweb';" | addrc + name "add default gitweb access" + echo "\$GL_WILDREPOS_DEFPERMS = 'R gitweb';" | addrc - runlocal git ls-remote u1:bar/u1/try4 - runremote ls -al repositories/bar/u1/try4.git/git-daemon-export-ok - expect "ls: cannot access repositories/bar/u1/try4.git/git-daemon-export-ok: No such file or directory" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 44 .* projects.list" - runremote cat projects.list - expect "bar/u1/try4.git" + runlocal git ls-remote u1:bar/u1/try4 + runremote ls -al repositories/bar/u1/try4.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try4.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 32 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 44 .* projects.list" + runremote cat projects.list + expect "bar/u1/try4.git" - name "add default both access" - echo "\$GL_WILDREPOS_DEFPERMS = 'R daemon gitweb';" | addrc + name "add default both access" + echo "\$GL_WILDREPOS_DEFPERMS = 'R daemon gitweb';" | addrc - runlocal git ls-remote u1:bar/u1/try5 - runremote ls -al repositories/bar/u1/try5.git/git-daemon-export-ok - expect "gitolite-test gitolite-test .* repositories/bar/u1/try5.git/git-daemon-export-ok" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 60 .* projects.list" - runremote cat projects.list - expect "bar/u1/try5.git" + runlocal git ls-remote u1:bar/u1/try5 + runremote ls -al repositories/bar/u1/try5.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try5.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 48 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 60 .* projects.list" + runremote cat projects.list + expect "bar/u1/try5.git" - name "add default both access with @all also" - echo "\$GL_WILDREPOS_DEFPERMS = 'R @all daemon gitweb';" | addrc + name "add default both access with @all also" + echo "\$GL_WILDREPOS_DEFPERMS = 'R @all daemon gitweb';" | addrc - runlocal git ls-remote u1:bar/u1/try6 - runremote ls -al repositories/bar/u1/try6.git/git-daemon-export-ok - expect "gitolite-test gitolite-test .* repositories/bar/u1/try6.git/git-daemon-export-ok" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 76 .* projects.list" - runremote cat projects.list - expect "bar/u1/try6.git" + runlocal git ls-remote u1:bar/u1/try6 + runremote ls -al repositories/bar/u1/try6.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try6.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 64 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 76 .* projects.list" + runremote cat projects.list + expect "bar/u1/try6.git" - name "remove all from u6" - < /dev/null runlocal ssh u1 setperms bar/u1/try6 + name "remove all from u6" + < /dev/null runlocal ssh u1 setperms bar/u1/try6 - runlocal git ls-remote u1:bar/u1/try6 - runremote ls -al repositories/bar/u1/try6.git/git-daemon-export-ok - expect "ls: cannot access repositories/bar/u1/try6.git/git-daemon-export-ok: No such file or directory" - runremote ls -al projects.list - expect "gitolite-test gitolite-test 60 .* projects.list" - runremote cat projects.list - notexpect "bar/u1/try6.git" + runlocal git ls-remote u1:bar/u1/try6 + runremote ls -al repositories/bar/u1/try6.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try6.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 48 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 60 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try6.git" - name "INTERNAL" + name "set default access to @all only" + echo "\$GL_WILDREPOS_DEFPERMS = 'R @all';" | addrc + runlocal git ls-remote u1:bar/u1/try7 + runremote ls -al repositories/bar/u1/try7.git/git-daemon-export-ok + [ "$ais" = "0" ] && expect "ls: cannot access repositories/bar/u1/try7.git/git-daemon-export-ok: No such file or directory" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test .* repositories/bar/u1/try7.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 48 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 76 .* projects.list" + runremote cat projects.list + [ "$ais" = "0" ] && notexpect "bar/u1/try7.git" + [ "$ais" = "1" ] && expect "bar/u1/try7.git" + + name "set default access to daemon only" + echo "\$GL_WILDREPOS_DEFPERMS = 'R daemon';" | addrc + + runlocal git ls-remote u1:bar/u1/try8 + runremote ls -al repositories/bar/u1/try8.git/git-daemon-export-ok + expect "gitolite-test gitolite-test .* repositories/bar/u1/try8.git/git-daemon-export-ok" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 48 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 76 .* projects.list" + runremote cat projects.list + notexpect "bar/u1/try8.git" + + name "set default access to gitweb only" + echo "\$GL_WILDREPOS_DEFPERMS = 'R gitweb';" | addrc + + runlocal git ls-remote u1:bar/u1/try9 + runremote ls -al repositories/bar/u1/try9.git/git-daemon-export-ok + expect "ls: cannot access repositories/bar/u1/try9.git/git-daemon-export-ok: No such file or directory" + runremote ls -al projects.list + [ "$ais" = "0" ] && expect "gitolite-test gitolite-test 64 .* projects.list" + [ "$ais" = "1" ] && expect "gitolite-test gitolite-test 92 .* projects.list" + runremote cat projects.list + expect "bar/u1/try9.git" + + name "INTERNAL" + + done done From 8808158bd8ae912272bb3c25625be347c3814781 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 24 Aug 2010 21:57:25 +0530 Subject: [PATCH 456/850] (minor) test script was misnamed --- t/{t61-setperms-wild => t61-setperms-groups} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename t/{t61-setperms-wild => t61-setperms-groups} (100%) diff --git a/t/t61-setperms-wild b/t/t61-setperms-groups similarity index 100% rename from t/t61-setperms-wild rename to t/t61-setperms-groups From 1d566ac46b9f46d6c521ae7fc6d7e1831eab53f5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 25 Aug 2010 23:10:31 +0530 Subject: [PATCH 457/850] a bit of optimisation to help another one along (thanks to reuss for telling me about the problem, although setting BIG_CONFIG worked just as well in his case, and this patch was not really needed) --- src/gl-compile-conf | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 0ccd7d5..13897f6 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -512,13 +512,18 @@ my %projlist = (); # for each real repo (and remember this will be empty, thus skipping all this, # if $GL_NO_DAEMON_NO_GITWEB is on!) + +# note: we do them in 2 separate loops to avoid breaking the optimisation in +# sub parse_acl (look for variable $saved_crwu) + for my $repo (keys %repo_patts) { - wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git"); - # daemon is easy &setup_daemon_access($repo); +} +for my $repo (keys %repo_patts) { + wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git"); # gitweb is a little more complicated. Here're some notes: # - "setup_gitweb_access" also sets "owner", despite the name # - specifying a description also counts as enabling gitweb @@ -535,7 +540,7 @@ for my $repo (keys %repo_patts) { # implementation note: this must happen *after* one of the previous 2 # calls (setup daemon or gitweb). The reason is that they call # "can_read", which eventually calls parse_acl with the right "creator" - # set for the *current* repo, which in turn stores translated values fr + # set for the *current* repo, which in turn stores translated values for # $creator in the repo_config hash, which, (phew!) is needed for a match # that eventually gets you a valid $repo_config{} below &setup_repo_configs($repo, \%repo_config) if $repo_config{$repo}; From c1bd3ca69c7323ac5dad4c11ff351feaff737c1c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 26 Aug 2010 19:55:49 +0530 Subject: [PATCH 458/850] umpteenth doc revamp... because someone else found the doc overwhelming. However, the suggested reading order (which so far existed only on the wiki) was probably a good thing to have at the top of the README, and the disclaimers about ssh may help keep my sanity a little longer ;-) --- README.mkd | 28 ++- doc/6-ssh-troubleshooting.mkd | 418 +++++++++++++++++----------------- doc/9-gitolite-and-ssh.mkd | 3 +- 3 files changed, 231 insertions(+), 218 deletions(-) diff --git a/README.mkd b/README.mkd index b88d0e5..80a4e4b 100644 --- a/README.mkd +++ b/README.mkd @@ -4,11 +4,27 @@ Gitolite is an access control layer on top of git, which allows access control down to the branch level, including specifying who can and cannot *rewind* a given branch. -This README will give you a quick intro. All documentation is available -within the source repo, in markdown format. If you want to read it *without* -cloning the source repo, just hit [this link][docs] and read nicely HTML-ised -versions of all the docs (automatic rendering of markdown to HTML courtesy -github). +Gitolite comes with a **huge** amount of documentation. If you're absolutely +new, the suggested reading order is this: + + * the README (this document) for a quick intro + * **IMPORTANT:** if you don't know anything about ssh, read [this][doc9gas] + * the [INSTALL][install] document + * if you run into trouble with ssh, read [this][doc6sts] + * the [ADMIN][admin] document + * if you're migrating from gitosis, read [this][migr] + +Once you've installed it and started using it, you'll want to explore some of +the more powerful features. All the documentation is available in the source +repo as well as [online][docs]. All the longer documents have tables of +contents, so you can quickly get a feel for what is covered right at the top. + +[install]: http://github.com/sitaramc/gitolite/blob/master/doc/0-INSTALL.mkd +[admin]: http://github.com/sitaramc/gitolite/blob/master/doc/2-admin.mkd +[migr]: http://github.com/sitaramc/gitolite/blob/master/doc/1-migrate.mkd +[docs]: http://github.com/sitaramc/gitolite/blob/pu/doc +[doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-gitolite-and-ssh.mkd +[doc6sts]: http://github.com/sitaramc/gitolite/blob/pu/doc/6-ssh-troubleshooting.mkd ---- @@ -150,5 +166,3 @@ Gitolite is released under GPL v2. See COPYING for details. sitaramc@gmail.com mailing list: gitolite@googlegroups.com - -[docs]: http://github.com/sitaramc/gitolite/blob/pu/doc diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 36e51d9..9e421c9 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -2,17 +2,16 @@ In this document: - * the most common problems that an admin will see - * basic ssh troubleshooting - * passphrases versus passwords - * ssh-agent problems - * basic ssh troubleshooting for the main admin - * basic ssh troubleshooting for a normal user + * (common) ssh asks for a password + * problems when using package, root, or non-root install methods + * problems when using the "from-client" install method + * (sidebar) why two keys on client for the admin + * bypassing gitolite without intending to + * basic ssh troubleshooting for the admin * windows issues * details * files on the server * files on client - * why two keys on client * some other tips and tricks * giving shell access to gitolite users * losing your admin key @@ -20,37 +19,205 @@ In this document: ---- -This document should help you troubleshoot ssh-related problems in accessing -gitolite *after* the install has completed successfully. +---- -In addition, I **strongly** recommend reading -[doc/9-gitolite-and-ssh.mkd][doc9gas], which is a very detailed look at how -gitolite uses ssh's features on the server side. Most people don't know ssh -as well as they *think* they do; even if you don't have any problems right -now, it's worth skimming over. +This document should help you troubleshoot ssh-related problems in installing +and accessing gitolite. -In addition to both these documents, there's now a program called -`sshkeys-lint` that you can run on your client. Run it without arguments to -get help on how to run it and what inputs it needs. +**This is about all the help I can give you in terms of the ssh aspect of +using gitolite. If you're installing gitolite, you're a "system admin", like +it or not. Ssh is therefore a necessary skill. Please take the time to learn +at least enough to get passwordless access working.** -**IMPORTANT NOTE: A lot of this document applies only if you installed using -the [from-client method][fc] . If you used one of the [other 3 methods][o3] , -some of it may or may not apply. At the very least, any mention of `ssh -gitolite` below will change to `ssh git@server`; see [this][urls] for why.** +**I have spent more than my share of time helping people debug their +misconfigured servers, simply because they tried to blame gitolite for their +troubles. This stops now. I'd rather spend time on actual gitolite features, +code, and documentation.** - +Other resources: -### the most common problems that an admin will see + * I **strongly** recommend reading [doc/9-gitolite-and-ssh.mkd][doc9gas], + which is a very detailed look at how gitolite uses ssh's features on the + server side. Most people don't know ssh as well as they *think* they do; + even if you don't have any problems right now, it's worth skimming over. -Ironically, these problems **only** happen to the person who installed -gitolite using easy-install (the "from-client" method in -[doc/0-INSTALL.mkd][doc0]), and has **utterly failed** to read/heed the -message that shows up at the end of running that command. This is because -only the admin has *two* ssh keys to the server (see "basic ssh -troubleshooting for the main admin" section below for more on this). + * there's a program called `sshkeys-lint` that you can run on your client. + Run it without arguments to get help on how to run it and what inputs it + needs. -Both these problems are caused by using the wrong key, thus **bypassing gitolite -completely**: +---- + + + +### (common) ssh asks for a password + +**NOTE**: This section should be useful to anyone trying to get password-less +access working. It is **not** specific to gitolite. + +You have generated a keypair on your workstation (`ssh-keygen`) and copied the +public part of it (`~/.ssh/id_rsa.pub`, by default) to the server. + +On the server you have appended this file to `~/.ssh/authorized_keys`. Or you +ran something, like the `gl-setup` or `gl-easy-install` steps during a +gitolite install, which should have done that for you. + +You now expect to log in without having to type in a password, but when you +try, you are being asked for a password. + +This is a quick checklist: + + * make sure you're being asked for a password and not a pass*phrase*. Do + not confuse or mistake a prompt saying `Enter passphrase for key + '/home/sitaram/.ssh/id_rsa':` for a password prompt from the remote + server! + + When you create an ssh keypair using `ssh-keygen`, you have the option of + protecting it with a passphrase. When you subsequently use that keypair + to access a remote host, your *local* ssh client needs to unlock the + corresponding private key, and ssh will probably ask for the passphrase + you set when you created the keypair. + + You have two choices to avoid this prompt every time you try to use the + private key. The first is to create keypairs *without* a passphrase (just + hit enter when prompted for one). **Be sure to add a passphrase later, + once everything is working, using `ssh-keygen -p`**. + + The second is to use `ssh-agent` (or `keychain`, which in turn uses + `ssh-agent`) or something like that to manage your keys. Other than + discussing one more potential trouble-spot with ssh-agent (see below), + further discussion of ssh-agent/keychain is out of scope of this document. + + * make sure the right private key is being offered. Run ssh in very + verbose mode and look for the word "Offering", like so: + + ssh -vvvv user@host pwd 2> >(grep -i offer) + + If some keys *are* being offered, but not the key that was supposed to be + used, you may be using ssh-agent; see next bullet. + + If you don't see any offers being made at all, then you probably don't + have any protocol 2 keys in your `~/.ssh` (names are `id_rsa` or `id_dsa`, + with corresponding `.pub` files). + + * If `ssh-add -l` responds with either "The agent has no identities." or + "Could not open a connection to your authentication agent.", then you can + skip this bullet. + + However, if `ssh-add -l` lists *any* keys at all, then something weird + happens. Due to a quirk in ssh-agent, ssh will now *only* use one of + those keys, *even if you explicitly ask* for some other key to be used. + + In that case, add the key you want using `ssh-add ~/.ssh/mykey` and try + the access again. + + * ssh is very sensitive to permissions. An extremely conservative setup is + given below, but be sure to do this on **both the client and the server**: + + cd $HOME + chmod go-rwx . + chmod -R go-rwx .ssh + + * actually, every component of the path to `~/.ssh/authorized_keys` all the + way upto the root directory must be at least `chmod go-w`. So be sure to + check `/` and `/home` also. + + * while you're doing this, make sure the owner and group info for each of + these components are correct. `ls -ald ~ ~/.ssh ~/.ssh/authorized_keys` + will tell you what they are. + + * if all that fails, log onto the server as root, `cd /var/log`, and look + for a file called `auth.log` or `secure` or some such name. Look inside + this file for messages matching the approximate time of your last attempt + to login, to see if they tell you what is the problem. + +---- + + + +### problems when using package, root, or non-root install methods + +This section applies if you installed using any of the [first 3 methods][o3] +of install. + +In these 3 modes, installation is done on the server, by logging in as some +other user and doing and `su - git`. The admin's workstation has only one key +that is known to the server's authkeys file, and this key invokes gitolite. +**Note** that this key is not supposed to get you a shell; it is supposed to +invoke gitolite. + +As a result, it's a lot easier to debug. Just run `ssh git@server info`. If +this get you the [gitolite version and access info][repout], everything is +fine. If it asks you for a password, see the very first section of this +document for help. + +If it gets you the GNU info command output, you have shell access. This +probably means you had passwordless shell access to the server *before* you +were added as a gitolite user, and you sent that same key to your gitolite +admin to include in the admin repo. This won't work -- the same key appears +twice in the authkeys file now, and since the ssh server will always use the +first match, the second occurrence (which invokes gitolite) is ignored. + +You'll have to (create and) use a different keypair for gitolite access. + + + +### problems when using the "from-client" install method + +This section applies if you installed using the [from-client method][fc]. + +This method of install is unique in that the admin will have 2 distinct keys +to access the server. The default key (`~/.ssh/id_rsa`) is used to get a +shell prompt and to run commands; for example, `gl-easy-install` uses this key +to do all its server-side work. + +In addition, there is a named key created just to invoke gitolite instead of +starting a shell. The name is whatever you gave as the third argument to the +`gl-easy-install` command (for example, `~/.ssh/sitaram.pub` in my case). + +Finally, a `host gitolite` para is added to `~/.ssh/config`: + + host gitolite + user git + hostname server + identityfile ~/.ssh/sitaram + +so that you can use `gitolite:reponame` as the URL to make ssh use the named +key. + +All this applies *only* to the admin. Normal users will only have one key and +do not need any of this. + + + +#### (sidebar) why two keys on client for the admin + +> There are two types of access the admin will make to the server: a normal +> login, to get a shell prompt, and gitolite access (clone/fetch/push etc). +> The first access needs an authkeys line *without* any "command=" +> restrictions, while the second requires a line *with* such a restriction. + +> And we can't use the same key for both because there is no way to +> disambiguate them; the ssh server will always (*always*) pick the first +> one in sequence when the key is offered by the ssh client. + +> So the next question is usually "I have other ways to get a shell on that +> account (like `su - git` from some other account), so why do I need a key +> for shell access at all?" + +> The answer to this is that the "easy install" script, being written for +> the most general case, needs shell access via ssh to do its stuff. If you +> have access otherwise, you really should use one of the other 3 install +> methods to install gitolite. Please see the [install doc][install] for +> details. + + + +#### bypassing gitolite without intending to + +These problems happen to the person who has **utterly failed** to read/heed +the message that shows up at the end of running the `gl-easy-install` command. +Both these problems are caused by using the wrong key, thus **bypassing +gitolite completely**: * you get `fatal: 'reponame' does not appear to be a git repository`, and yet you are sure 'reponame' exists, you haven't mis-spelled it, etc. @@ -90,95 +257,14 @@ the repo and clones ok. But when you push, gitolite's **update hook** kicks in, and fails to run because you some of the environment variables it is expecting are not present. - + -### basic ssh troubleshooting - -[glb]: http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh - -I assume the gitolite server is called "server" and the user hosting all the -gitolite repos is "git". I will also be using "sitaram" as the *gitolite -username* of the admin. - -Unless specifically mentioned, all these commands are run on the user's or -admin's workstation, not on the server. - - - -#### passphrases versus passwords - -When you create an ssh keypair, you have the option of protecting it with a -passphrase. When you subsequently use that keypair to access a remote host, -your *local* ssh client needs to unlock the corresponding private key, and ssh -will probably ask for the passphrase you set when you created the keypair. - -Do not confuse or mistake this prompt (`Enter passphrase for key -'/home/sitaram/.ssh/id_rsa':`) for a password prompt from the remote server! - -You have two choices to avoid this prompt every time you try to access the -remote. The first is to create keypairs *without* a passphrase (just hit -enter when prompted for one). **Be sure to add a passphrase later, once -everything is working, using `ssh-keygen -p`**. - -The second is to use `ssh-agent` (or `keychain`, which in turn uses -`ssh-agent`) or something like that to manage your keys. Other than the next -section, further discussion of this is out of scope of this document. - - - -#### ssh-agent problems - -1. Run `ssh-add -l`. If this responds with either "The agent has no - identities." or "Could not open a connection to your authentication - agent.", skip this section. - -2. However, if it lists some keys, like this: - - 2048 fc:c1:48:1e:06:31:97:a4:8b:fc:37:b2:76:14:c7:53 /home/sitaram/.ssh/id_rsa (RSA) - 2048 d2:e0:7f:fa:1a:89:22:41:bb:06:d9:ff:a7:27:36:5c /home/sitaram/.ssh/sitaram (RSA) - - then run `ls ~/.ssh` and make sure that all the keypairs you have there - are represented in the `ssh-add -l` output. - -3. If you find any keypairs in `~/.ssh` that are not represented in the - `ssh-add -l` output, add them. For instance, if `ssh-add -l` showed me - only the `id_rsa` key, but I also had a `sitaram` (and `sitaram.pub`) - keypair, I'd run `ssh-add ~/.ssh/sitaram` to add it. - -This is because ssh-agent has a quirk: if `ssh-add -l` shows *any* keys at -all, ssh will only use those keys. Even if you explicitly specify an unlisted -key using `ssh -i` or an `identityfile` directive in the config file, it won't -use it. - - - -#### basic ssh troubleshooting for the main admin - -You're the "main admin" if you're trying to access gitolite from the same -workstation and user account where you ran the "easy install" command. You -should have two keypairs in your `~/.ssh` directory. The pair called `id_rsa` -(and `id_rsa.pub`) was probably the first one you created, and you used this -to get passwordless (pubkey based) access to the server (which was a -pre-requisite for running the easy install command). - -The second keypair has the same name as the last argument in the easy install -command you ran (in my case, `sitaram` and `sitaram.pub`). It was probably -created by the easy install script, and is the key used for gitolite access. - -In addition, you should have a "gitolite" paragraph in your `~/.ssh/config`, -looking something like this: - - host gitolite - user git - hostname server - identityfile ~/.ssh/sitaram - -If any of these are not true, you did something funky in your install; email -me or hop onto #git and hope for the best ;-) +#### basic ssh troubleshooting for the admin Otherwise, run these checks: -1. `ssh git@server` should get you a command line. +1. `ssh git@server` should get you a command line *without* asking for a + password. If it asks you for a password, then your `id_rsa` keypair changed after you ran the easy install, or someone fiddled with the @@ -198,7 +284,7 @@ The most generic way (and therefore time-taking) is to re-install gitolite from scratch: * make a backup of your gitolite-admin repo clone somewhere (basically your - "keydir/*.pub" and your "conf/gitolite.conf"). If necessary get these + `keydir/*.pub` and your `conf/gitolite.conf`). If necessary get these files from the server's `~/.gitolite` directory. * log on to the server somehow (using some other account, using a password, su-ing in, etc) and delete `~/.ssh/authorized_keys`. Rename or move aside @@ -220,23 +306,6 @@ from scratch: That's a long sequence but it should work. - - -#### basic ssh troubleshooting for a normal user - -For a normal user, life is much simpler. They should have only one pubkey, -which was previously sent to the gitolite admin to add into the admin repo's -`keydir` as "user.pub", and then "user" given permissions to some repo. - -`ssh git@server info` should get you [gitolite version and access -info][repout]. If it asks you for a password, your pubkey was not sent to the -server properly. Check with your admin. - -If it gets you the GNU info command output, you have shell access. This means -you had command line access to the server *before* you were added as a -gitolite user. If you send that same key to your gitolite admin to include in -the admin repo, it won't work. For reasons why, see below. - ### windows issues @@ -311,82 +380,13 @@ Here's how it all hangs together. ~/.ssh/sitaram ~/.ssh/sitaram.pub - * config file; this file has an entry for gitolite access: + * config file; this file has an entry for gitolite access if you install + usine the "from-client" method. (See above for example "host gitolite" + para in the ssh config file). - ~/.ssh/config - - To understand why we need that, let's step back a bit. Normally, you - might expect to access gitolite repos like this: - - ssh://git@server/reponame.git - - But this won't work, because this ends up using the *default* keypair - (normally), which gives you a command line. Which means it won't invoke - the `gl-auth-command` program at all, and so none of gitolite's access - control will work. - - - - You need to force ssh to use the *other* keypair when performing a git - operation. With normal ssh, that would be - - ssh -i ~/.ssh/sitaram git@server - - but git does not support putting an alternate keypair in the URL. - - Luckily, ssh has a very convenient way of capturing all the connection - information (username, hostname, port number (if it's not the default 22), - and keypair to be used) in one "paragraph" of `~/.ssh/config`. This is - what the para looks like for us (the easy install script puts it there the - first time): - - host gitolite - user git - hostname server - identityfile ~/.ssh/sitaram - - (The "gitolite" can be anything you want of course; it's like a group name - for all the stuff below it). This ensures that typing - - ssh gitolite - - is equivalent to - - ssh -i ~/.ssh/sitaram git@server - - and therefore this: - - git clone gitolite:reponame.git - - now works as expected, invoking the special keypair instead of the default - one. - - - -#### why two keys on client - -[This section is only applicable to installs done using the "from-client" -method; see [doc/0-INSTALL.mkd][doc0] for details]. - -Why do I (the admin) need two **different** keypairs? - -There are two types of access the admin will make to the server: a normal -login, to get a shell prompt, and gitolite access (clone/fetch/push etc). The -first access needs an authkeys line *without* any "command=" restrictions, -while the second requires a line *with* such a restriction. - -And we can't use the same key for both because there is no way to disambiguate -them; the ssh server will always (*always*) pick the first one in sequence -when the key is offered by the ssh client. - -So the next question is usually "I have other ways to get a shell on that -account (like `su - git` from some other account), so why do I need a key for -shell access at all?" - -The answer to this is that the "easy install" script, being written for the -most general case, needs shell access via ssh to do its stuff. If you have -access otherwise, you really should use one of the other 3 install methods to -install gitolite. Please see the [install doc][install] for details. + This is needed because this is the only way to force git to use a + non-default keypair (unlike command line ssh, which can be given the `-i + ~/.ssh/sitaram` flag to do so). diff --git a/doc/9-gitolite-and-ssh.mkd b/doc/9-gitolite-and-ssh.mkd index a03636f..26087dd 100644 --- a/doc/9-gitolite-and-ssh.mkd +++ b/doc/9-gitolite-and-ssh.mkd @@ -1,6 +1,6 @@ # how gitolite uses ssh -Here's a secret: ***gitolite uses far more ssh magic than git magic***! +***Gitolite is heavily dependent on ssh***! Most people didn't realise this, and even if they did they didn't know ssh well enough to help themselves. If you don't understand how ssh public key @@ -152,4 +152,3 @@ a special update hook. This hook takes all the information presented, looks at the config file, and decides to allow or reject the update. And that's basically it. - From 6c48647c15d79058560230ba2babe555b29a271a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 1 Sep 2010 12:37:11 +0530 Subject: [PATCH 459/850] (more ssh documentation shenanigans) --- doc/6-ssh-troubleshooting.mkd | 5 +++ doc/7-install-transcript.mkd | 63 ++++++++++++++++++++++++++++------- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 9e421c9..4589577 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -36,6 +36,10 @@ code, and documentation.** Other resources: + * people who think this is too hard should take a look at + [doc/7-install-transcript.mkd][doc7] to **see how simple it *actually* + is**. + * I **strongly** recommend reading [doc/9-gitolite-and-ssh.mkd][doc9gas], which is a very detailed look at how gitolite uses ssh's features on the server side. Most people don't know ssh as well as they *think* they do; @@ -461,3 +465,4 @@ bigger problems than gitolite install not working!)] [fc]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#from_client_method_install_from_the_client_to_the_server [urls]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#URLs_for_gitolite_managed_repos [repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd +[doc7]: http://github.com/sitaramc/gitolite/blob/pu/doc/7-install-transcript.mkd diff --git a/doc/7-install-transcript.mkd b/doc/7-install-transcript.mkd index 1ebf116..4730123 100644 --- a/doc/7-install-transcript.mkd +++ b/doc/7-install-transcript.mkd @@ -1,10 +1,30 @@ # gitolite install transcript +In this document: + + * about this document + * create userids on server and client (optional) + * get pubkey access from client to server + * get gitolite source + * install gitolite + * VERY IMPORTANT... + * examine what you have + * emergency password access + +---- + + + +### about this document + This is a *complete* transcript of a full gitolite install, *from scratch*, using brand new userids ("sita" on the client, "git" on the server). Please note that you can use existing userids also, it is not necessary to use -dedicated user IDs for this. Also, you don't have to use some *other* server -for all this, both server and client can be "localhost" if you like. +dedicated user IDs for this. In particular, people who have a single user +hosting account can also use this method, as long as they have password access +as a fallback if they screw up the keys somewhere. Also, you don't have to +use some *other* server for all this, both server and client can be +"localhost" if you like. Please note that this entire transcript can be summarised as: @@ -18,14 +38,6 @@ transcript is **non**-gitolite stuff :) **Please also note that this method will setup everything on the server, but you have to run it on your workstation, NOT on the server!** -In this document: - - * create userids on server and client (optional) - * get pubkey access from client to server - * get gitolite source - * install gitolite - * examine what you have - ---- @@ -209,10 +221,27 @@ install mode that allows you to change the defaults etc. ---- + + +### VERY IMPORTANT... + +Please read the text that the easy-install command produces as output when you +run it. People who fail to read this get into trouble later. And I didn't +write all that because I wanted to practice typing. + +The text just above this section is an approximation; your version will +contain the correct URLs for your install, including port numbers if +non-standard ports were used). + +Try out that `tail -31 ./gl-easy-install` too :) + ### examine what you have +The last step of the previous command creates a local clone of your +gitolite-admin repo in `~/gitolite-admin`. + sita@sita-lt:src $ cd ~/gitolite-admin/ sita@sita-lt:gitolite-admin $ git --no-pager log --stat @@ -227,5 +256,15 @@ install mode that allows you to change the defaults etc. 2 files changed, 13 insertions(+), 0 deletions(-) And that's really all. Add keys to keydir here, edit conf/gitolite.conf as -needed, then add, commit, and push the changes to the server. Try out that -`tail -31 ./gl-easy-install` too :) +needed, then add, commit, and push the changes to the server. + + + +### emergency password access + +If you lose your keys or the worst happens and you use the wrong key for the +wrong thing and apparently lose all access, but you still know the password, +this is what you do: + + sita@sita-lt:~ $ ssh -o preferredauthentications=password git@server + git@server's password: From bebc67eba37e187ed420e2c4e7f0a7894066bb0f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 3 Sep 2010 08:51:58 +0530 Subject: [PATCH 460/850] towel day program was not limiting commits as stated in message --- src/gl-dont-panic | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gl-dont-panic b/src/gl-dont-panic index 5bdd398..9476c96 100755 --- a/src/gl-dont-panic +++ b/src/gl-dont-panic @@ -73,8 +73,8 @@ then git clone $REPO_BASE/gitolite-admin.git $TEMPDIR cd $TEMPDIR - echo printing the previous 9 commits to the config; echo - git log --date=relative --format="%h %ar%x09%s" | perl -pe 'print "$.\t"' + echo printing the last 9 commits to the config; echo + git log -9 --date=relative --format="%h %ar%x09%s" | perl -pe 'print "$.\t"' echo; read -p 'please enter how many commits you want to rewind: ' n good=`git rev-parse --short HEAD~$n` From 5188ec48ed37eb2c26c7a902a3a2d0e688b12c33 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 3 Sep 2010 08:51:16 +0530 Subject: [PATCH 461/850] fix a comment typo which totally changed the meaning! --- src/gl-auth-command | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 45e6ced..958eb5b 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -195,7 +195,7 @@ if ($perm =~ /C/) { # it was missing, and you have create perms wrap_chdir("$ENV{GL_REPO_BASE_ABS}"); new_repo($repo, "$GL_ADMINDIR/hooks/common", $user); - # note pwd is not the bare "repo.git"; new_repo does that... + # note pwd is now the bare "repo.git"; new_repo does that... wrap_print("gl-perms", "$GL_WILDREPOS_DEFPERMS\n") if $GL_WILDREPOS_DEFPERMS; &setup_repo_configs($repo, \%repo_config); &setup_daemon_access($repo); From 07d07cc5f6ad79f963a2820bcac5f3b8098ce116 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 3 Sep 2010 08:48:51 +0530 Subject: [PATCH 462/850] rmrepo alternative 2 was too lax it would have allowed deleting a non-wild repo too! Fixed. --- contrib/adc/rmrepo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/adc/rmrepo b/contrib/adc/rmrepo index b35a422..42976f4 100755 --- a/contrib/adc/rmrepo +++ b/contrib/adc/rmrepo @@ -13,6 +13,8 @@ get_rights_and_owner $delete # alternative 2 -- creator or anyone with W access to admin repo can delete [ "$owner" = "$GL_USER" ] || { + # but only if it is wild + [ "$owner" = "" ] && die "$delete is not a wild repo" get_rights_and_owner gitolite-admin [ -z "$perm_write" ] && die "$delete is not yours to delete and you're not the admin" } From 2cf382ca4ecefa4bb36f6f145333a7b21f84a808 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 3 Sep 2010 08:52:14 +0530 Subject: [PATCH 463/850] (minor) some doc typos --- doc/4-wildcard-repositories.mkd | 2 +- doc/6-ssh-troubleshooting.mkd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/4-wildcard-repositories.mkd b/doc/4-wildcard-repositories.mkd index 759367d..e9a506b 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/4-wildcard-repositories.mkd @@ -186,7 +186,7 @@ The following points are important: ### setting a gitweb description for a wildcard-matched repo -Similar to the getperm/setperm commands, there are the getdesc/setdesc +Similar to the getperms/setperms commands, there are the getdesc/setdesc commands, thanks to Teemu. diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 4589577..7ae6616 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -258,7 +258,7 @@ The second error happens if you use `git@server:repositories/reponame.git` (assuming default `$REPO_BASE` setting) -- that is, you used the full unix path. Since the "prefixing" mentioned above is not required, the shell finds the repo and clones ok. But when you push, gitolite's **update hook** kicks -in, and fails to run because you some of the environment variables it is +in, and fails to run because some of the environment variables it is expecting are not present. From 3559c1190e8f5094df2d1f967f19eb727c6d7929 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 3 Sep 2010 16:46:51 +0530 Subject: [PATCH 464/850] autotoc deletes/re-creates only its own anchors so you can add your own --- contrib/autotoc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/autotoc b/contrib/autotoc index ca85eb4..9ddbe7e 100755 --- a/contrib/autotoc +++ b/contrib/autotoc @@ -16,13 +16,14 @@ sub make_anchor { undef $/; my $doc = <>; -$doc =~ s/^<\/a>\n\n//mg; +$doc =~ s/^<\/a>\n\n//mg; +$doc =~ s/^<\/a>\n\n//mg; my @toc = $doc =~ /^###+ .*/mg; -$doc =~ s/^(###+) (.*)/"<\/a>\n\n$1 $2"/mge; +$doc =~ s/^(###+) (.*)/"<\/a>\n\n$1 $2"/mge; for (@toc) { - s/^(###+) (.*)/' ' x (length($1)-3) . ' * $2<\/a>"/e; + s/^(###+) (.*)/' ' x (length($1)-3) . ' * $2<\/a>"/e; } my $toc = "In this document:\n\n"; From 5f342c0444771ba21a2aeb05fff307590e10b79b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 2 Sep 2010 19:15:32 +0530 Subject: [PATCH 465/850] more doc revamp; some notes below - all anchors prefixed by AUTO_ now - some bad links fixed (maybe still a few I didn't catch) - misc wording changes/additions (support section to README, "technical skills" section to install doc, etc). --- README.mkd | 85 +++++--- doc/{0-INSTALL.mkd => 1-INSTALL.mkd} | 184 ++++++++++++------ doc/2-admin.mkd | 84 ++++---- doc/3-faq-tips-etc.mkd | 177 ++++++++--------- doc/admin-defined-commands.mkd | 43 ++-- doc/big-config.mkd | 28 +-- doc/{5-delegation.mkd => delegation.mkd} | 29 ++- ...olite-and-ssh.mkd => gitolite-and-ssh.mkd} | 16 +- doc/hook-propagation.mkd | 40 ++-- ...-transcript.mkd => install-transcript.mkd} | 34 ++-- doc/{1-migrate.mkd => migrate.mkd} | 4 +- doc/mirroring.mkd | 62 +++--- doc/{9-packaging.mkd => packaging.mkd} | 0 doc/progit-article.mkd | 18 +- doc/report-output.mkd | 18 +- ...leshooting.mkd => ssh-troubleshooting.mkd} | 80 ++++---- doc/{9-uninstall.mkd => uninstall.mkd} | 0 ...sitories.mkd => wildcard-repositories.mkd} | 50 +++-- 18 files changed, 527 insertions(+), 425 deletions(-) rename doc/{0-INSTALL.mkd => 1-INSTALL.mkd} (54%) rename doc/{5-delegation.mkd => delegation.mkd} (82%) rename doc/{9-gitolite-and-ssh.mkd => gitolite-and-ssh.mkd} (92%) rename doc/{7-install-transcript.mkd => install-transcript.mkd} (89%) rename doc/{1-migrate.mkd => migrate.mkd} (98%) rename doc/{9-packaging.mkd => packaging.mkd} (100%) rename doc/{6-ssh-troubleshooting.mkd => ssh-troubleshooting.mkd} (87%) rename doc/{9-uninstall.mkd => uninstall.mkd} (100%) rename doc/{4-wildcard-repositories.mkd => wildcard-repositories.mkd} (86%) diff --git a/README.mkd b/README.mkd index 80a4e4b..6e91578 100644 --- a/README.mkd +++ b/README.mkd @@ -1,3 +1,5 @@ + + # gitolite Gitolite is an access control layer on top of git, which allows access control @@ -8,45 +10,39 @@ Gitolite comes with a **huge** amount of documentation. If you're absolutely new, the suggested reading order is this: * the README (this document) for a quick intro - * **IMPORTANT:** if you don't know anything about ssh, read [this][doc9gas] * the [INSTALL][install] document - * if you run into trouble with ssh, read [this][doc6sts] * the [ADMIN][admin] document - * if you're migrating from gitosis, read [this][migr] + +If you run into trouble start [here](#support). If you're migrating from +gitosis, read [this][migr]. Once you've installed it and started using it, you'll want to explore some of the more powerful features. All the documentation is available in the source repo as well as [online][docs]. All the longer documents have tables of contents, so you can quickly get a feel for what is covered right at the top. -[install]: http://github.com/sitaramc/gitolite/blob/master/doc/0-INSTALL.mkd -[admin]: http://github.com/sitaramc/gitolite/blob/master/doc/2-admin.mkd -[migr]: http://github.com/sitaramc/gitolite/blob/master/doc/1-migrate.mkd -[docs]: http://github.com/sitaramc/gitolite/blob/pu/doc -[doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-gitolite-and-ssh.mkd -[doc6sts]: http://github.com/sitaramc/gitolite/blob/pu/doc/6-ssh-troubleshooting.mkd - ---- In this document: - * what - * why - * other features - * security - * contact and license + * what + * why + * main features + * support + * security + * contact and license ---- - + ### what -Gitolite allows a server to host many git repositories and provide access to -many developers, without having to give them real userids on or shell access -to the server. The essential magic in doing this is ssh's pubkey access and -the `authorized_keys` file, and the inspiration was an older program called -gitosis. +Gitolite lets you use a single user on a server to host many git repositories +and provide access to many developers, without having to give them real +userids on or shell access to the server. The essential magic in doing this +is ssh's pubkey access and the `authorized_keys` file, and the inspiration was +an older program called gitosis. Gitolite can restrict who can read from (clone/fetch) or write to (push) a repository. It can also restrict who can push to what branch or tag, which is @@ -55,7 +51,7 @@ requiring root permissions, and with no additional software than git itself and perl. It also has several other neat features described below and elsewhere in the [doc/][docs] directory. - + ### why @@ -74,9 +70,9 @@ allow/disallow users access to repos. But there are several disadvantages here: * every user needs a userid and password on the server. This is usually a - killer...! + killer, especially in tightly controlled environments * adding/removing access rights involves complex `usermod -G ...` mumblings - which most admins would rather not deal with, thanks to you-know-who + which most admins would rather not deal with * *viewing* (aka auditing) the current set of permissions requires running multiple commands to list directories and their permissions/ownerships, users and their group memberships, and then correlating all these manually @@ -102,9 +98,9 @@ Gitolite does away with all this: read-write access, not only at the repository level, but at the branch level within repositories. - + -### other features +### main features The most important feature I needed was **per-branch permissions**. This is pretty much mandatory in a corporate environment, and is almost the single @@ -136,7 +132,26 @@ detail somewhere in gitolite's [doc/][docs] subdirectory. * specify repos using patterns (patterns may include creator's name) * define powerful operations on the server side, even github-like forking - + + + + +### support + +Most installation problems are caused by not knowing ssh. Take a look at this +[transcript][] to see how simple it actually is, if your server's ssh daemon +is behaving itself. + +If I suspect your problem is an ssh issue, I will probably ignore it. Please +learn how [gitolite uses ssh][doc9gas] and then methodically go through the +[ssh trouble shooting][doc6sts] document. These two documents contain +everything I could possibly tell you. I have nothing to add. + +Even for other topics, please look through at least the table of contents of +at least the numbered documents to see if your question is already answered, +before asking. + + ### security @@ -158,11 +173,21 @@ details, looking for the word "security". ---- - + ### contact and license Gitolite is released under GPL v2. See COPYING for details. -sitaramc@gmail.com -mailing list: gitolite@googlegroups.com + * author: sitaramc@gmail.com, sitaram@atc.tcs.com + * mailing list: gitolite@googlegroups.com + * list subscribe address : gitolite+subscribe@googlegroups.com + +[transcript]: http://github.com/sitaramc/gitolite/blob/pu/doc/install-transcript.mkd +[install]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd +[admin]: http://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd +[migr]: http://github.com/sitaramc/gitolite/blob/pu/doc/migrate.mkd +[docs]: http://github.com/sitaramc/gitolite/blob/pu/doc +[doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite-and-ssh.mkd +[doc6sts]: http://github.com/sitaramc/gitolite/blob/pu/doc/ssh-troubleshooting.mkd + diff --git a/doc/0-INSTALL.mkd b/doc/1-INSTALL.mkd similarity index 54% rename from doc/0-INSTALL.mkd rename to doc/1-INSTALL.mkd index 8c0f2f4..ae801c4 100644 --- a/doc/0-INSTALL.mkd +++ b/doc/1-INSTALL.mkd @@ -2,40 +2,42 @@ In this document: - * please read this first - * important notes - * conventions used - * requirements - * client side - * server side - * installation and setup - * (package method) directly on the server, using RPM/DEB - * (root method) directly on the server, manually, with root access - * (non-root method) directly on the server, manually, without root access - * (from-client method) install from the client to the server - * URLs for gitolite-managed repos - * special cases -- multiple gitolite servers - * package method and root method - * from-client method - * upgrading - * uninstalling - * cleaning out a botched install - * uninstalling gitolite completely + * please read this first + * important notes + * conventions used + * requirements + * client/workstation + * server + * technical skills + * installation and setup + * install methods and deciding which one to use + * (package method) directly on the server, using RPM/DEB + * (root method) directly on the server, manually, with root access + * (non-root method) directly on the server, manually, without root access + * (from-client method) install from the client to the server + * URLs for gitolite-managed repos + * special cases -- multiple gitolite servers + * package method and root method + * from-client method + * upgrading + * uninstalling + * cleaning out a botched install + * uninstalling gitolite completely ---- - + ### please read this first - + #### important notes Please make sure you understand the following points first. - * gitolite is somewhat unusual as far as "server" software goes -- every - userid on the server is a potential "gitolite host". + * gitolite runs as a single user on a server, and is invoked via ssh. Thus, + every user on the server is a potential "gitolite host". * gitolite depends **heavily** on ssh pubkey (passwordless) access. Do not assume you know all about ssh -- most people **don't**. If in doubt, use @@ -43,23 +45,45 @@ Please make sure you understand the following points first. administration of gitolite. To make matters worse, ssh problems in gitolite don't always look like ssh - problems. See [doc/6-ssh-troubleshooting.mkd][doc6] for help. + problems. See [doc/ssh-troubleshooting.mkd][doc6] for help. - +A gitolite setup has: + + * a server + * a "hosting user" on the server -- the userid under which gitolite runs. + You can have any number of "hosting users" on one server; in fact every + user can host their own gitolite instance + * an "admin user" -- the user who sets up gitolite and configures it + * the admin user's client or workstation, from which he does all his work + +It is possible to have the server and the client be the same machine, and even +the admin user be also the hosting user, (i.e., `sitaram@server` can install +and administer a gitolite setup running under `sitaram@server`, a situation +that is common with some hosting services). It's actually fairly easy and +**safe** to do, **as long as you have password access to the server** for +emergency use. However, I will not be documenting it because (a) if you know +ssh you'll know how to extrapolate my instructions to do this and (b) if you +don't know ssh it'll be a nightmare to support you. + + #### conventions used -We assume the admin user is "sitaram", and his workstation is called "client". -The hosting user is "git", and the server is called "server". Substitute your -values as needed. +Throughout the documentation, we use "sitaram" as the admin user, and his +workstation is called "client". The hosting user is "git", and the server is +called "server". **Please substitute your values as needed**. - +Also, we often say "the rc file". This means `~/.gitolite.rc` on the server. +And when we say the "access control rules", or "conf file", or "config file", +we mean `conf/gitolite.conf` on your gitolite-admin clone. + + #### requirements - + -##### client side +##### client/workstation * git version 1.6.2 or greater * even msysgit on Windows is fine; please don't ask me for help if @@ -69,9 +93,9 @@ values as needed. shell is needed * again, msysgit on Windows is fine - + -##### server side +##### server * any Unix system with a posix compatible "sh". * people using "csh" or derivatives please don't ask me for help -- tell @@ -82,11 +106,47 @@ values as needed. * perl (but since git requires it anyway, you probably have it) * openssh or any ssh that can understand the `authorized_keys` file format - + + +##### technical skills + + * if you're installing gitolite, you're a "system admin", like it or not. + Ssh is therefore a necessary skill. Please take the time to learn at + least enough to get passwordless access working. + + * you also need to be somewhat familiar with git itself. You cannot + administer a whole bunch of git repositories if you don't know the basics + of git. + + * some familiarity with Unix and shells is probably required + + * regular expressions are a big part of gitolite in many places but + familiarity is not necessary to do basic access control. + + ### installation and setup - + + + + +#### install methods and deciding which one to use + +Gitolite has 4 install methods: + + * **package method** if you have a gitolite RPM or a DEB available + * **root method** if you have root access to the server, and you plan to + have multiple "hosting users" on it + * **non-root method** if you don't have root access to the server, but you + do have at least one account with a password + * **from-client method** if you are not comfortable with public keys and + server side commands + +Here's how you install using these 3 methods. Future upgrades are equally +easy -- the steps required for upgrading are marked "(U)". + + #### (package method) directly on the server, using RPM/DEB @@ -100,7 +160,7 @@ values as needed. * on the client, run `cd; git clone git@server:gitolite-admin` - + #### (root method) directly on the server, manually, with root access @@ -122,7 +182,7 @@ values as needed. * on the client, run `cd; git clone git@server:gitolite-admin` - + #### (non-root method) directly on the server, manually, without root access @@ -151,22 +211,25 @@ server so that if you screw up the keys you can still get on, or be able to * on the client, run `cd; git clone git@server:gitolite-admin` - + + + #### (from-client method) install from the client to the server -This used to be the most common install method for a long time, and it is -still the one I use for most of my testing. The **main advantage** of this -method is that it **forces you** to solve the ssh pubkey problem **before** -attempting to install. Sadly, it also forces the admin to use a **different** -URL for gitolite repos than normal users, which seems to confuse a heck of a -lot of people who don't read the prominently displayed messages and/or the -documentation. +The advantage of this method is that it forces you to solve the ssh pubkey +problem **before** attempting to install. It works best if you have dedicated +userids, one on the server for installing gitolite, and one the client for +administering it. -This method is verbosely documented in [doc/7-install-transcript.mkd][doc7], -including *outputs* of the commands concerned. +Sadly, it also forces the admin to use a different URL to access gitolite +repos than normal users, which seems to confuse a heck of a lot of people who +don't read the prominently displayed messages and/or the documentation. - +This method is verbosely documented in this [transcript][], including +*outputs* of the commands concerned. + + ### URLs for gitolite-managed repos @@ -181,11 +244,11 @@ However, in the fourth ("from-client") method, the admin user needs a different URL (`gitolite:reponame`) to gain access to the gitolite repositories. Check [here][twokeys] for why. - + ### special cases -- multiple gitolite servers - + #### package method and root method @@ -209,7 +272,7 @@ to have a command line on the server, so don't give them the passwords if you don't need to -- the pubkey will allow them to be gitolite admins on their domain, and that's quite enough for normal operations. - + #### from-client method @@ -229,7 +292,7 @@ have been created as respective clones. Or you can re-clone elsewhere: cd ~/admin1; git clone gitolite_server_1:gitolite-admin.git cd ~/admin2; git clone gitolite_server_2:gitolite-admin.git - + ### upgrading @@ -244,11 +307,11 @@ also. Also, remember that some new features may require additional settings in your `~/.gitolite.rc` file. - + ### uninstalling - + #### cleaning out a botched install @@ -266,18 +329,17 @@ here's how to clean the slate. * remove `~/.gitolite`, `~/.gitolite.rc` and `~/repositories/gitolite-admin.git` - + #### uninstalling gitolite completely There's some duplication between this and the previous section, but uninstalling gitolite is described in great detail in -[doc/9-uninstall.mkd][doc9unin] +[doc/uninstall.mkd][doc9unin] ---- -[doc6]: http://github.com/sitaramc/gitolite/blob/pu/doc/6-ssh-troubleshooting.mkd -[doc7]: http://github.com/sitaramc/gitolite/blob/pu/doc/7-install-transcript.mkd -[doc9unin]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-uninstall.mkd -[doc9ssh]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-ssh-tips.mkd -[twokeys]: http://github.com/sitaramc/gitolite/blob/pu/doc/6-ssh-troubleshooting.mkd#why_two_keys_on_client +[doc6]: http://github.com/sitaramc/gitolite/blob/pu/doc/ssh-troubleshooting.mkd +[doc9unin]: http://github.com/sitaramc/gitolite/blob/pu/doc/uninstall.mkd +[twokeys]: http://github.com/sitaramc/gitolite/blob/pu/doc/ssh-troubleshooting.mkd#twokeys +[transcript]: http://github.com/sitaramc/gitolite/blob/pu/doc/install-transcript.mkd diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 9b2a7be..5f979c2 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -1,38 +1,46 @@ # administering and running gitolite +In this document: + + * please read this first + * adding users and repos + * other features + * moving pre-existing repos into gitolite + * specifying gitweb and daemon access + * custom hooks + * hook chaining + * environment variables available to hooks + * custom git config + +---- + + + +### please read this first + +Unless you know what you're doing, do not do **anything** manually on the +server, like adding new repositories or users or changing the access control +rules. Things will break. For example, if you manually create a repo on the +server, it will not have the required "update" hook, without which there is no +access control for pushes. + +Most normal (day-to-day) gitolite admin work is done by cloning the +gitolite-admin repo from the server to your workstation, making changes to the +clone, and pushing those changes back. + +The installation steps in the previous section include the steps to do this +clone, so you should already have one on your workstation, in +`~/gitolite-admin`. You can of course clone it anywhere else you want and use +that clone. + +Either way, make sure you `cd` into this clone first. + *Note*: some of the paths in this document use variable names. Just refer to `~/.gitolite.rc` for the correct values for *your* installation. -In this document: - - * please read this first - * adding users and repos - * other features - * moving pre-existing repos into gitolite - * specifying gitweb and daemon access - * custom hooks - * hook chaining - * environment variables available to hooks - * custom git config - ----- - - - -### please read this first - - * ***do NOT add new repos manually***, unless you know how to add the - required hook as well. Without the hook, branch-level access control will - not work for that repo, which sorta defeats the idea of using gitolite :-) - - * most normal (day-to-day) gitolite admin work is done by cloning the - gitolite-admin repo from the server to your workstation, making changes to - the clone, and pushing those changes back. The installation steps in - [doc/0-INSTALL.mkd][doc0] include the steps to do this clone if needed. - Once you've cloned it, you're ready to add users and repos. - + ### adding users and repos @@ -57,11 +65,11 @@ Once you've cloned it, you're ready to add users and repos. automatically be created (empty, but clonable) and users' access will be updated as needed. - + ### other features - + #### moving pre-existing repos into gitolite @@ -93,7 +101,7 @@ to take a bunch of existing repos and add them to gitolite: `conf/gitolite.conf` in your gitolite-admin repo clone. Then add, commit, push. - + #### specifying gitweb and daemon access @@ -102,10 +110,7 @@ like unauthenticated access of any kind to any repo!), but someone wanted it, so here goes. To make a repo or repo group accessible via "git daemon", just give read -permission to the special user "daemon". See the [faq, tips, etc][ss] -document for easy ways to specify access for multiple repositories. - -[ss]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gwd +permission to the special user "daemon". There's a special user called "gitweb" also, which works the same way. However, setting a description for the project also enables gitweb permissions @@ -131,7 +136,7 @@ The "compile" script will keep these files consistent with the config settings -- this includes removing such settings/files if you remove "read" permissions for the special usernames or remove the description line. - + #### custom hooks @@ -150,7 +155,7 @@ implements all the branch-level permissions in gitolite. If you fiddle with the hooks directory, please make sure you do not mess with this file accidentally, or all your fancy per-branch permissions will stop working.** - + #### hook chaining @@ -173,7 +178,7 @@ Finally, these names (`update.secondary` and `post-update.secondary`) are merely the defaults. You can change them to anything you want; look in conf/example.gitolite.rc for details. - + #### environment variables available to hooks @@ -189,7 +194,7 @@ The following variables are also set, but are generally less useful: * `GL_BINDIR` -- where all the binaries live * `GL_ADMINDIR` -- common directory for many gitolite things - + #### custom git config @@ -213,6 +218,5 @@ basic forms of the "git config" command: It does not (currently) support other options like `--add`, the `value_regex`, etc. -[doc0]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd [genpub]: http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 4a65e2e..466471f 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -2,48 +2,48 @@ In this document: - * common errors and mistakes - * git version dependency - * other errors, warnings, notes... - * cloning an empty repo - * `@all` syntax for repos - * umask setting - * getting a tar file from a clone - * features - * syntax and normal usage - * simpler syntax - * one user, many keys - * security, access control, and auditing - * two levels of access rights checking - * better logging - * "exclude" (or "deny") rules - * separating delete and rewind rights - * separating create and push rights - * file/dir NAME based restrictions - * delegating parts of the config file - * convenience features - * what repos do I have access to? - * including config lines from other files - * support for git installed outside default PATH - * "personal" branches - * custom hooks and custom git config - * bypassing gitolite - * INconvenience features - * deleting a repo - * helping with gitweb - * easier to specify gitweb "description" and gitweb/daemon access - * easier to link gitweb authorisation with gitolite - * advanced features - * repos named with wildcards - * admin defined commands - * access control for external commands - * svnserve - * design choices - * keeping the parser and the access control separate + * common errors and mistakes + * git version dependency + * other errors, warnings, notes... + * cloning an empty repo + * `@all` syntax for repos + * umask setting + * getting a tar file from a clone + * features + * syntax and normal usage + * simpler syntax + * one user, many keys + * security, access control, and auditing + * two levels of access rights checking + * better logging + * "exclude" (or "deny") rules + * separating delete and rewind rights + * separating create and push rights + * file/dir NAME based restrictions + * delegating parts of the config file + * convenience features + * what repos do I have access to? + * including config lines from other files + * support for git installed outside default PATH + * "personal" branches + * custom hooks and custom git config + * bypassing gitolite + * INconvenience features + * deleting a repo + * helping with gitweb + * easier to specify gitweb "description" and gitweb/daemon access + * easier to link gitweb authorisation with gitolite + * advanced features + * repos named with wildcards + * admin defined commands + * access control for external commands + * svnserve + * design choices + * keeping the parser and the access control separate ---- - + ### common errors and mistakes @@ -65,19 +65,19 @@ In this document: In other words, you used a key that completely bypassed gitolite and went straight to the shell to do the clone. - Please see doc/6-ssh-troubleshooting.mkd for what all this means. + Please see doc/ssh-troubleshooting.mkd for what all this means. - + ### git version dependency Gitolite (on the server) now refuses to run if git is not at least 1.6.2. - + ### other errors, warnings, notes... - + #### cloning an empty repo @@ -90,7 +90,7 @@ end hung up unexpectedly`. However, you can ignore this, since it doesn't seem to hurt anything. [Update 2009-09-14; this has been fixed in git 1.6.4.3] - + #### `@all` syntax for repos @@ -102,14 +102,14 @@ There *is* a way to use the `@all` syntax for repos also, as described in access, we do not support this. * don't try giving `@all` users some permission for `@all` repos - + #### umask setting Gitweb not able to read your repos? You can change the umask for newly created repos to something more relaxed -- see the `~/.gitolite.rc` file - + ### getting a tar file from a clone @@ -125,7 +125,7 @@ plain "git archive", because the Makefile adds a file called make master.tar # or maybe "make pu.tar" - + ### features @@ -133,11 +133,11 @@ Apart from the big ones listed in the top level README, and subjective ones like "better config file format", gitolite has evolved to have many useful features than the original goal of branch-level access control. - + #### syntax and normal usage - + ##### simpler syntax @@ -182,7 +182,9 @@ do not worry that this causes some duplication or inefficiency. It doesn't See the "specify gitweb/daemon access" section below for one more example. - + + + ##### one user, many keys @@ -227,11 +229,11 @@ corresponding derived usernames (which is what goes into the sitaramc@gmail.com@laptop.pub sitaramc@gmail.com sitaramc@gmail.com@desktop.pub sitaramc@gmail.com - + #### security, access control, and auditing - + ##### two levels of access rights checking @@ -267,7 +269,7 @@ any of the refexes match, the push succeeds. If none of them match, it fails. Gitolite also allows "exclude" or "deny" rules. See later in this document for details. - + ##### better logging @@ -295,7 +297,7 @@ The other parts of the log line are the name of the repo, the refname being updated, the user updating it, and the refex pattern (from the config file) that matched, in case you need to debug the config file itself. - + ##### "exclude" (or "deny") rules @@ -346,7 +348,7 @@ And here's how it works: before the third one, and it has a `-` as the permission, so the push fails - + ##### separating delete and rewind rights @@ -386,9 +388,7 @@ message when you push, a non-existant user. Note 3: you can combine this with the "create a branch" permissions described in the next section, as the example line in conf/example.conf shows. -[sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c# - - + ##### separating create and push rights @@ -406,7 +406,7 @@ Briefly: Note: you can combine this with the "delete a branch" permissions described in the previous section, as the example line in conf/example.conf shows. - + ##### file/dir NAME based restrictions @@ -417,20 +417,18 @@ changed, treating each filename as a "ref" to be matched. Please see `conf/example.conf` for syntax and examples. - + ##### delegating parts of the config file You can now split up the config file and delegate the authority to specify -access control for their own pieces. See -[doc/5-delegation.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/5-delegation.mkd) -for details. +access control for their own pieces. See [delegation][] for details. - + #### convenience features - + ##### what repos do I have access to? @@ -441,15 +439,13 @@ etc. You'd just like a simple way to know what repos you have access to. Gitolite provides two commands (`info` and `expand`) to help you find this information; please check [doc/report-output.mkd][repout] for details. -[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd - - + ##### including config lines from other files See the entry under "INCLUDE SOME OTHER FILE" in `conf/example.conf`. - + ##### support for git installed outside default PATH @@ -483,7 +479,7 @@ the full PATH in the rc file, like so: $ENV{PATH} = "/home/sitaram/bin:$ENV{PATH}"; - + ##### "personal" branches @@ -507,7 +503,7 @@ important thing is that the "branch" name should contain `/USER/` (including the slashes). At runtime this will match whoever is the current user. Access is still determined by the right hand side of course. - + ##### custom hooks and custom git config @@ -515,7 +511,7 @@ You can specify hooks that you want to propagate to all repos, as well as per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and `conf/example.conf` for details. - + ##### bypassing gitolite @@ -538,11 +534,11 @@ to set that variable permanently, preferring this mode instead: GL_BYPASS_UPDATE_HOOK=1 git push - + #### INconvenience features - + ##### deleting a repo @@ -550,8 +546,6 @@ By design, there is no code in gitolite to *delete* a repo if the repo was specified by name in the config file. (Wildcard repos *can* be deleted by the user; see [here][rmrepo] for details). -[rmrepo]: http://github.com/sitaramc/gitolite/blob/pu/doc/admin-defined-commands.mkd#rmrepo - If you *do* want to permanently delete a *non*-wildcard repo, here's what you do: @@ -561,7 +555,7 @@ do: * *then* remove the repo from `~/repositories` on the server (or whatever you set `$GL_REPO_BASE` to in the `~/.gitolite.rc`) - + #### helping with gitweb @@ -569,7 +563,9 @@ Although gitweb is a completely separate program, gitolite can do quite a lot to help you manage gitweb access as well; once the initial setup is complete, you can do it all from within the gitolite config file! - + + + ##### easier to specify gitweb "description" and gitweb/daemon access @@ -618,7 +614,7 @@ Here's an example, using really short reponames because I'm lazy: repo r2 # ...and so on... - + ##### easier to link gitweb authorisation with gitolite @@ -654,24 +650,24 @@ Gitweb allows you to specify a subroutine to decide on access. We use that feature and tie it to gitolite. Configuration example can be found in `contrib/gitweb/`. - + #### advanced features - + ##### repos named with wildcards -Please see `doc/4-wildcard-repositories.mkd` for all the details. +Please see `doc/wildcard-repositories.mkd` for all the details. - + ##### admin defined commands This requires the wildcards feature to be enabled, but is then an extremely powerful feature. See `doc/admin-defined-commands.mkd`. - + ##### access control for external commands @@ -681,7 +677,7 @@ server-side command that contains enough information to make an access control decision. Note that this is incompatible with giving people shell access as described in -`doc/6-ssh-troubleshooting.mkd` -- people who have shell access are not +`doc/ssh-troubleshooting.mkd` -- people who have shell access are not subject to this mechanism (it wouldn't make sense to try and control someone who has shell access anyway). @@ -695,7 +691,7 @@ Commands implemented so far are: * svnserve (see next section for a brief description; this has been contributed by Simon and Vladimir) - + ###### svnserve @@ -707,11 +703,11 @@ system. Assuming you installed gitolite to the same user as the one you used for SVN, SVN connectivity will be retained, and users will be able to use both SVN and git using the same SSH configuration. - + ### design choices - + #### keeping the parser and the access control separate @@ -732,3 +728,8 @@ have to be first "compiled", and the access control programs use this If you choose the "easy install" method, all this is quite transparent to you anyway. If you cannot use the easy install and must install manually, I have clear instructions on how to set it up. + +[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd +[sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c# +[delegation]: http://github.com/sitaramc/gitolite/blob/pu/doc/delegation.mkd +[rmrepo]: http://github.com/sitaramc/gitolite/blob/pu/doc/admin-defined-commands.mkd#rmrepo diff --git a/doc/admin-defined-commands.mkd b/doc/admin-defined-commands.mkd index 61e4b6a..36f7b9e 100644 --- a/doc/admin-defined-commands.mkd +++ b/doc/admin-defined-commands.mkd @@ -11,18 +11,18 @@ There may be other such **WARNING** sections below. **Read all of them**. In this document: - * background - * setting it up - * anatomy of a command - * example uses and sample commands in contrib - * fork - * rmrepo - * enable/disable push access temporarily - * (bonus) restricted admin + * background + * setting it up + * anatomy of a command + * example uses and sample commands in contrib + * fork + * rmrepo + * enable/disable push access temporarily + * (bonus) restricted admin ---- - + ### background @@ -36,9 +36,6 @@ resisted the urge to point him to [this][xkcd224], told him that's a great idea and he should go for it, mentally blessing him for letting me off the hook on coding it ;-) [Laziness][lazy] *is* the first virtue you know! -[xkcd224]: http://xkcd.com/224/ -[lazy]: http://c2.com/cgi/wiki?LazinessImpatienceHubris - And that was that. For a couple of days. Soon, though, I realised that there could be a pretty big bonus in this for @@ -47,13 +44,11 @@ on "restricted admin" for what's really exciting about this for *me*. ---- -It may be a good idea to read [doc/4-wildcard-repositories.mkd][wild] before +It may be a good idea to read [doc/wildcard-repositories.mkd][wild] before you continue here though, because most of the uses of this feature also need wildcard repos. (This also means you must set `$GL_WILDREPOS` to "1" in the rc file). -[wild]: http://github.com/sitaramc/gitolite/blob/pu/doc/4-wildcard-repositories.mkd - The wildcard repo feature is a way to create repositories matching a pattern (even if it as simple as `personal/CREATOR/.+`), and a way to specify two categories of permissions for each such user-created repo. @@ -62,7 +57,7 @@ What we want now is more than that, as you'll see in the examples below. And I'm sure if you think of more uses you'll send them to me as "contrib" entries, right? - + ### setting it up @@ -82,7 +77,7 @@ to inadvertently *hide* some of the "official" commands (like "info", executable files with those names in this directory. So don't do that -- you have been warned!** - + ### anatomy of a command @@ -137,11 +132,11 @@ convenient. See any of the other samples for how to use it. If you don't like this, roll your own. If you don't like bash, do the eqvt in your language of choice. - + ### example uses and sample commands in contrib - + #### fork @@ -169,6 +164,8 @@ or some such incantation. + + #### rmrepo This is one thing that you really could not do before this setup was created. @@ -179,7 +176,7 @@ Use it like this: The script checks to make sure that the repo being deleted was *created* by the user invoking it. - + #### enable/disable push access temporarily @@ -217,7 +214,7 @@ in doc/2. You need code like this in `update.secondary` (don't forget to exit 0 - + #### (bonus) restricted admin @@ -239,3 +236,7 @@ script to proceed. [Note that this particular use does not require `$GL_WILDREPOS` to be enabled, because it's not using any wildcard repos]. + +[xkcd224]: http://xkcd.com/224/ +[lazy]: http://c2.com/cgi/wiki?LazinessImpatienceHubris +[wild]: http://github.com/sitaramc/gitolite/blob/pu/doc/wildcard-repositories.mkd diff --git a/doc/big-config.mkd b/doc/big-config.mkd index bbe126d..b2f35b0 100644 --- a/doc/big-config.mkd +++ b/doc/big-config.mkd @@ -2,13 +2,13 @@ In this document: - * when/why do we need it? - * how do we use it? - * other optimisations - * what are the downsides? - * (extra coolness) usergroups and LDAP/similar tools + * when/why do we need it? + * how do we use it? + * other optimisations + * what are the downsides? + * (extra coolness) usergroups and LDAP/similar tools - + ### when/why do we need it? @@ -95,7 +95,7 @@ Phew! You can imagine what that does when you have 10,000 users and 10,000 repos. Let's just say it's not pretty :) - + ### how do we use it? @@ -143,7 +143,7 @@ looks like this: That's a lot smaller, and allows orders of magintude more repos and groups to be supported. - + ### other optimisations @@ -158,8 +158,8 @@ first one): `GL_NO_DAEMON_NO_GITWEB` is a very useful optimisation that you *must* enable if you *do* have a large number of repositories, and do *not* use gitolite's support for gitweb or git-daemon access (see "[easier to specify gitweb -description and gitweb/daemon access][gw]" for details). This will save a lot -of time when you push the gitolite-admin repo with changes. This variable +description and gitweb/daemon access][gwd]" for details). This will save a +lot of time when you push the gitolite-admin repo with changes. This variable also control whether "git config" lines (such as `config hooks.emailprefix = "[gitolite]"`) will be processed or not. @@ -176,9 +176,7 @@ Also note that using all 3 of the `GL_NO_*` variables will result in *everything* after the config compile being skipped. In other words, gitolite is being used **only** for its access control language. -[gw]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gitweb - - + ### what are the downsides? @@ -192,7 +190,7 @@ subset of the allowed @fragname, which would work normally, do not work now). (If you didn't understand all that, you're probably not using delegation, so feel free to ignore it!) - + ### (extra coolness) usergroups and LDAP/similar tools @@ -238,3 +236,5 @@ specified: @foo = alice @bar = alice + +[gwd]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gwd diff --git a/doc/5-delegation.mkd b/doc/delegation.mkd similarity index 82% rename from doc/5-delegation.mkd rename to doc/delegation.mkd index 8cc341f..c711bf6 100644 --- a/doc/5-delegation.mkd +++ b/doc/delegation.mkd @@ -6,25 +6,22 @@ In this document: - * lots of repos, lots of users - * splitting up the set of repos into groups - * delegating ownership of groups of repos - * security/philosophy note + * lots of repos, lots of users + * splitting up the set of repos into groups + * delegating ownership of groups of repos + * security/philosophy note ---- - + ### lots of repos, lots of users Gitolite tries to make it easy to manage access to lots of users and repos, -exploiting commonalities wherever possible. (The example in [this -section][ss] should give you an idea). As you can see, it lets you specify -bits and pieces of the access control separately -- i.e., *all* the access -specs for a certain repo need not be together; they can be scattered, which -makes it easier to manage the sort of slice and dice needed in that example. - -[ss]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#simpler_syntax +exploiting commonalities wherever possible. It lets you specify bits and +pieces of the access control separately -- i.e., *all* the access specs for a +certain repo need not be together; they can be scattered, which makes it +easier to manage the sort of slice and dice needed in that example. But eventually the config file will become too big. If you let only one person have control, he could become a bottleneck. If you give it to multiple @@ -47,7 +44,7 @@ access control rules for a set of repos they have been given authority for. It's easier to show how it all works with an example instead of long descriptions. - + ### splitting up the set of repos into groups @@ -63,7 +60,7 @@ or repos, same syntax). So the basic idea is that the main config file # any other config as usual, including access control lines for any of the # above projects or groups - + ### delegating ownership of groups of repos @@ -117,7 +114,7 @@ to the bottom of the main file. ---- - + ### security/philosophy note @@ -136,4 +133,4 @@ If you feel the need to delegate even that, please just go the whole hog and give them separate gitolite instances! It's pretty easy to setup the *software* itself system-wide, so that many users can use it without all the "easy install" fuss. See the "system install / user setup" section in -doc/0-INSTALL.mkd for details. +doc/1-INSTALL.mkd for details. diff --git a/doc/9-gitolite-and-ssh.mkd b/doc/gitolite-and-ssh.mkd similarity index 92% rename from doc/9-gitolite-and-ssh.mkd rename to doc/gitolite-and-ssh.mkd index 26087dd..f4bd74b 100644 --- a/doc/9-gitolite-and-ssh.mkd +++ b/doc/gitolite-and-ssh.mkd @@ -13,14 +13,14 @@ blaming ***git/gitolite*** for whatever is going wrong with your setup :-) In this document: - * ssh basics - * how does gitolite use all this ssh magic? - * restricting shell access/distinguishing one user from another - * restricting branch level actions + * ssh basics + * how does gitolite use all this ssh magic? + * restricting shell access/distinguishing one user from another + * restricting branch level actions ---- - + ### ssh basics @@ -88,7 +88,7 @@ from somewhere, or maybe buy the OReilly ssh book. **This is the backbone of what makes gitolite work; please make sure you understand this** - + ### how does gitolite use all this ssh magic? @@ -98,7 +98,7 @@ These are two different questions you ought to be having by now: logging in as the same remote user "git" * how does it restrict what I can do within a repository - + #### restricting shell access/distinguishing one user from another @@ -131,7 +131,7 @@ at its config file, and either allows or rejects the request. But this cannot differentiate between different branches within a repo; that has to be done separately. - + #### restricting branch level actions diff --git a/doc/hook-propagation.mkd b/doc/hook-propagation.mkd index 44ef8da..97cb3ce 100644 --- a/doc/hook-propagation.mkd +++ b/doc/hook-propagation.mkd @@ -6,17 +6,17 @@ put them, what takes precedence. I'll try and set out the logic here. In this document: - * hooks used by gitolite - * **where** do I (the admin) put the hooks? - * the "from-client" method - * the other 3 methods - * the `GL_PACKAGE_HOOKS` directory - * the `$HOME/.gitolite` directory - * why two places? - * special case: the "non-root" method - * **when** do hooks propagate? + * hooks used by gitolite + * **where** do I (the admin) put the hooks? + * the "from-client" method + * the other 3 methods + * the `GL_PACKAGE_HOOKS` directory + * the `$HOME/.gitolite` directory + * why two places? + * special case: the "non-root" method + * **when** do hooks propagate? - + ### hooks used by gitolite @@ -32,7 +32,7 @@ In addition there is a "sentinel file" -- an empty file called The final objective of all this is that each repo's `hooks/` directory should get all the hooks that it is meant to get. - + ### **where** do I (the admin) put the hooks? @@ -44,9 +44,9 @@ Now we'll discuss the locations of these `hooks/common` and `hooks/gitolite-admin` directories. This depends on which install method you used. -(Please refer to [doc/0-INSTALL.mkd][0inst] for what these "methods" are). +(Please refer to [doc/1-INSTALL.mkd][0inst] for what these "methods" are). - + #### the "from-client" method @@ -57,11 +57,11 @@ gitolite clone on the client side. This is where you run method; skip to the next section ("when do hooks propagate") if you installed using the "from-client" method. - + #### the other 3 methods - + ##### the `GL_PACKAGE_HOOKS` directory @@ -80,7 +80,7 @@ process does the equivalent of `gl-system-install`. So now we know there's a location called `$GL_PACKAGE_HOOKS` where you can place your hooks. - + ##### the `$HOME/.gitolite` directory @@ -90,7 +90,7 @@ directory, which also contains a `hooks/` directory. So now there are two places you can put your hooks, apparently. - + #### why two places? @@ -116,7 +116,7 @@ get copied to `$HOME/.gitolite/hooks` when you "install". I need to fix and thoroughly test this later; for now, just ignore the extra files you see in there; they're harmless/redundant (TODO)] - + #### special case: the "non-root" method @@ -127,7 +127,7 @@ existed to cater to the "package" and "root" methods. In this method, the *strongly* suggest putting them in `$GL_PACKAGE_HOOKS` and ignoring `$HOME/.gitolite` completely. - + ### **when** do hooks propagate? @@ -171,5 +171,5 @@ For people who do not want certain hooks to run for certain repos, one simple solution that will work right now is to check the value of `$GL_REPO` at the start of the hook, and `exit 0` based on what it contains/matches. -[0inst]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd +[0inst]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd diff --git a/doc/7-install-transcript.mkd b/doc/install-transcript.mkd similarity index 89% rename from doc/7-install-transcript.mkd rename to doc/install-transcript.mkd index 4730123..67093ed 100644 --- a/doc/7-install-transcript.mkd +++ b/doc/install-transcript.mkd @@ -2,18 +2,18 @@ In this document: - * about this document - * create userids on server and client (optional) - * get pubkey access from client to server - * get gitolite source - * install gitolite - * VERY IMPORTANT... - * examine what you have - * emergency password access + * about this document + * create userids on server and client (optional) + * get pubkey access from client to server + * get gitolite source + * install gitolite + * VERY IMPORTANT... + * examine what you have + * emergency password access ---- - + ### about this document @@ -40,7 +40,7 @@ you have to run it on your workstation, NOT on the server!** ---- - + ### create userids on server and client (optional) @@ -87,7 +87,7 @@ because I'm not showing the actual "vi" session): ---- - + ### get pubkey access from client to server @@ -136,9 +136,11 @@ Double check to make sure you can log on to `git@server` without a password: sita@sita-lt:~ $ ssh git@server pwd /home/git +**DO NOT PROCEED UNTIL THIS WORKS OK!** + ---- - + ### get gitolite source @@ -150,7 +152,7 @@ Double check to make sure you can log on to `git@server` without a password: Receiving objects: 100% (1157/1157), 270.08 KiB | 61 KiB/s, done. Resolving deltas: 100% (756/756), done. - + ### install gitolite @@ -221,7 +223,7 @@ install mode that allows you to change the defaults etc. ---- - + ### VERY IMPORTANT... @@ -235,7 +237,7 @@ non-standard ports were used). Try out that `tail -31 ./gl-easy-install` too :) - + ### examine what you have @@ -258,7 +260,7 @@ gitolite-admin repo in `~/gitolite-admin`. And that's really all. Add keys to keydir here, edit conf/gitolite.conf as needed, then add, commit, and push the changes to the server. - + ### emergency password access diff --git a/doc/1-migrate.mkd b/doc/migrate.mkd similarity index 98% rename from doc/1-migrate.mkd rename to doc/migrate.mkd index 641f9c5..dd27b8b 100644 --- a/doc/1-migrate.mkd +++ b/doc/migrate.mkd @@ -36,8 +36,6 @@ Here's how we migrated my work repos: Now, log off the server and get back to the client: -[inst]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd - 1. follow instructions to install gitolite; see the [install document][inst]. Make sure that you **don't** change the default path for `$REPO_BASE` if you edit the config file! @@ -87,4 +85,4 @@ Now, log off the server and get back to the client: 5. Check all your changes to your gitolite-admin clone, commit, and push [mk]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#multikeys - +[inst]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd diff --git a/doc/mirroring.mkd b/doc/mirroring.mkd index 40988b0..382f95f 100644 --- a/doc/mirroring.mkd +++ b/doc/mirroring.mkd @@ -9,7 +9,7 @@ update, you just add a post-receive hook that says But life is never that simple... **This document has been tested using a 3-server setup, all installed using -the "non-root" method (see doc/0-INSTALL.mkd). However, the process is +the "non-root" method (see doc/1-INSTALL.mkd). However, the process is probably not going to be very forgiving of human error -- like anything that is this deep in "system admin" territory, errors are likely to be costly. If you're the kind who hits enter first and then thinks about what he typed, @@ -22,23 +22,23 @@ never *really* lost until you do a `git gc`**. In this document: - * RULE NUMBER ONE! - * things that will NOT be mirrored by this process - * conventions in this document - * setting up mirroring - * install gitolite on all servers - * generate keypairs - * setup the mirror-shell on each server - * set slaves to slave mode - * set slave server lists - * syncing the mirrors the first time - * switching over - * the return of foo - * switching back - * making foo a slave - * URLs that your users will use + * RULE NUMBER ONE! + * things that will NOT be mirrored by this process + * conventions in this document + * setting up mirroring + * install gitolite on all servers + * generate keypairs + * setup the mirror-shell on each server + * set slaves to slave mode + * set slave server lists + * syncing the mirrors the first time + * switching over + * the return of foo + * switching back + * making foo a slave + * URLs that your users will use - + ### RULE NUMBER ONE! @@ -52,7 +52,7 @@ Corollary: if the primary went down and you effected a changeover, you must make sure that the primary does not come up in a push-enabled mode when it recovers. - + ### things that will NOT be mirrored by this process @@ -72,7 +72,7 @@ important, (especially the gl-creator, although if your wildcard pattern had Your best bet is to use rsync for the log files, and tar for the others, at regular intervals. - + ### conventions in this document @@ -80,11 +80,11 @@ The userid hosting gitolite is `gitolite` on all machines. The servers are foo, bar, and baz. At the beginning, foo is the master, the other 2 are slaves. - + ### setting up mirroring - + #### install gitolite on all servers @@ -99,7 +99,7 @@ slaves. * Use the same "admin key" on all the machines, so that the same person has gitolite-admin access to all of them. - + #### generate keypairs @@ -108,7 +108,7 @@ servers, so first generate keypairs for all of them (`ssh-keygen`) and copy the `.pub` files to all other servers, named appropriately. So foo will have bar.pub and baz.pub, etc. - + #### setup the mirror-shell on each server @@ -136,7 +136,7 @@ Now test this access: Similarly test the other combinations. - + #### set slaves to slave mode @@ -145,7 +145,7 @@ Set slave mode on all the *slave* servers by setting `$GL_SLAVE_MODE = 1` Leave the master server's file as is. - + #### set slave server lists @@ -181,7 +181,7 @@ port number: And that's really all there is, unless... - + ### syncing the mirrors the first time @@ -193,7 +193,7 @@ them up once. gl-mirror-sync gitolite@bar # path to "sync" program is ~/.gitolite/src if "from-client" install - + ### switching over @@ -225,11 +225,11 @@ to have "baz" be a slave. system comes up. Better still, use extraneous means to block incoming connections from normal users (out of scope for this document). - + ### the return of foo - + #### switching back @@ -260,7 +260,7 @@ Switching back is fairly easy. * on foo, set slave mode off * tell everyone to switch back - + #### making foo a slave @@ -281,7 +281,7 @@ this, but YMMV. ---- - + ### URLs that your users will use diff --git a/doc/9-packaging.mkd b/doc/packaging.mkd similarity index 100% rename from doc/9-packaging.mkd rename to doc/packaging.mkd diff --git a/doc/progit-article.mkd b/doc/progit-article.mkd index 48bccbb..ce967f8 100644 --- a/doc/progit-article.mkd +++ b/doc/progit-article.mkd @@ -4,6 +4,8 @@ Git has started to become very popular in corporate environments, which tend to Gitolite allows you to specify permissions not just by repository, but also by branch or tag names within each repository. That is, you can specify that certain people (or groups of people) can only push certain "refs" (branches or tags) but not others. + + ### Installing ### Installing Gitolite is very easy, even if you don't read the extensive documentation that comes with it. You need an account on a Unix server of some kind; various Linux flavours, and Solaris 10, have been tested. You do not need root access, assuming git, perl, and an openssh compatible ssh server are already installed. In the examples below, we will use the `gitolite` account on a host called `gitserver`. @@ -35,10 +37,14 @@ And you're done! Gitolite has now been installed on the server, and you now hav That last command does produce a fair amount of output, which might be interesting to read. Also, the first time you run this, a new keypair is created; you will have to choose a passphrase or hit enter for none. Why a second keypair is needed, and how it is used, is explained in the "ssh troubleshooting" document that comes with Gitolite. (Hey the documentation has to be good for *something*!) + + ### Customising the Install ### While the default, quick, install works for most people, there are some ways to customise the install if you need to. If you omit the `-q` argument, you get a "verbose" mode install -- detailed information on what the install is doing at each step. The verbose mode also allows you to change certain server-side parameters, such as the location of the actual repositories, by editing an "rc" file that the server uses. This "rc" file is liberally commented so you should be able to make any changes you need quite easily, save it, and continue. This file also contains various settings that you can change to enable or disable some of gitolite's advanced features. + + ### Config File and Access Control Rules ### Once the install is done, you switch to the `gitolite-admin` repository (placed in your HOME directory) and poke around to see what you got: @@ -98,6 +104,8 @@ There are two levels of access control in gitolite. The first is at the reposit The second level, applicable only to "write" access, is by branch or tag within a repository. The username, the access being attempted (`W` or `+`), and the refname being updated are known. The access rules are checked in order of appearance in the config file, looking for a match for this combination (but remember that the refname is regex-matched, not merely string-matched). If a match is found, the push succeeds. A fallthrough results in access being denied. + + ### Advanced Access Control with "deny" rules ### So far, we've only seen permissions to be one or `R`, `RW`, or `RW+`. However, gitolite allows another permission: `-`, standing for "deny". This gives you a lot more power, at the expense of some complexity, because now fallthrough is not the *only* way for access to be denied, so the *order of the rules now matters*! @@ -110,6 +118,8 @@ Let us say, in the situation above, we want engineers to be able to rewind any b Again, you simply follow the rules top down until you hit a match for your access mode, or a deny. Non-rewind push to master or integ is allowed by the first rule. A rewind push to those refs does not match the first rule, drops down to the second, and is therefore denied. Any push (rewind or non-rewind) to refs other than master or integ won't match the first two rules anyway, and the third rule allows it. + + ### Restricting pushes by files changed ### In addition to restricting what branches a user can push changes to, you can also restrict what files they are allowed to touch. For example, perhaps the Makefile (or some other program) is really not supposed to be changed by just anyone, because a lot of things depend on it or would break if the changes are not done *just right*. You can tell gitolite: @@ -123,6 +133,8 @@ In addition to restricting what branches a user can push changes to, you can als This powerful feature is documented in `conf/example.conf`. + + ### Personal Branches ### Gitolite also has a feature called "personal branches" (or rather, "personal branch namespace") that can be very useful in a corporate environment. @@ -133,9 +145,13 @@ This would normally cause the same branch name clutter as in a centralised VCS, Gitolite lets you define a "personal" or "scratch" namespace prefix for each developer (for example, `refs/personal//*`); see the "personal branches" section in `doc/3-faq-tips-etc.mkd` for details. + + ### "Wildcard" repositories ### -Gitolite allows you to specify repositories with wildcards (actually perl regexes), like, for example `assignments/s[0-9][0-9]/a[0-9][0-9]`, to pick a random example. This is a *very* powerful feature, which has to be enabled by setting `$GL_WILDREPOS = 1;` in the rc file. It allows you to assign a new permission mode ("C") which allows users to create repositories based on such wild cards, automatically assigns ownership to the specific user who created it, allows him/her to hand out R and RW permissions to other users to collaborate, etc. This feature is documented in `doc/4-wildcard-repositories.mkd`. +Gitolite allows you to specify repositories with wildcards (actually perl regexes), like, for example `assignments/s[0-9][0-9]/a[0-9][0-9]`, to pick a random example. This is a *very* powerful feature, which has to be enabled by setting `$GL_WILDREPOS = 1;` in the rc file. It allows you to assign a new permission mode ("C") which allows users to create repositories based on such wild cards, automatically assigns ownership to the specific user who created it, allows him/her to hand out R and RW permissions to other users to collaborate, etc. This feature is documented in `doc/wildcard-repositories.mkd`. + + ### Other Features ### diff --git a/doc/report-output.mkd b/doc/report-output.mkd index 255cab6..e85db22 100644 --- a/doc/report-output.mkd +++ b/doc/report-output.mkd @@ -10,14 +10,14 @@ through this document). In this document: - * the "info" command - * interpreting the output - * using patterns to limit output - * the "expand" command + * the "info" command + * interpreting the output + * using patterns to limit output + * the "expand" command ---- - + ### the "info" command @@ -52,7 +52,7 @@ is often blank. @R_ @W_ testing R W vkc - + #### interpreting the output @@ -77,7 +77,7 @@ suffixed by a symbol: The `_` suffix is special. This says the user has only implicit access (due to one of the `@all` uses), but no explicit access. - + #### using patterns to limit output @@ -103,7 +103,7 @@ The pattern is also mandatory when an admin wants to find out what access some *other* user has, which you may have guessed from the syntax in the "usage" line above. - + ### the "expand" command @@ -119,6 +119,6 @@ For each repo found, it searches for it in the config -- either the actual repo entry (when the repo is not a wildcard repo), or an entry for the wildcard that matches it -- and reports permissions. It also takes into account extra permissions enabled by the `setperms` command (see -doc/4-wildcard-repositories.mkd). It shows you the "creator" of the repo as +doc/wildcard-repositories.mkd). It shows you the "creator" of the repo as an additional column, defaulting to `` if it was not a wildcard repo. diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/ssh-troubleshooting.mkd similarity index 87% rename from doc/6-ssh-troubleshooting.mkd rename to doc/ssh-troubleshooting.mkd index 7ae6616..59570a2 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/ssh-troubleshooting.mkd @@ -2,20 +2,20 @@ In this document: - * (common) ssh asks for a password - * problems when using package, root, or non-root install methods - * problems when using the "from-client" install method - * (sidebar) why two keys on client for the admin - * bypassing gitolite without intending to - * basic ssh troubleshooting for the admin - * windows issues - * details - * files on the server - * files on client - * some other tips and tricks - * giving shell access to gitolite users - * losing your admin key - * simulating ssh-copy-id + * (common) ssh asks for a password + * problems when using package, root, or non-root install methods + * problems when using the "from-client" install method + * (sidebar) why two keys on client for the admin + * bypassing gitolite without intending to + * basic ssh troubleshooting for the admin + * windows issues + * details + * files on the server + * files on client + * some other tips and tricks + * giving shell access to gitolite users + * losing your admin key + * simulating ssh-copy-id ---- @@ -36,11 +36,10 @@ code, and documentation.** Other resources: - * people who think this is too hard should take a look at - [doc/7-install-transcript.mkd][doc7] to **see how simple it *actually* - is**. + * people who think this is too hard should take a look at this + [transcript][] to **see how simple it *actually* is**. - * I **strongly** recommend reading [doc/9-gitolite-and-ssh.mkd][doc9gas], + * I **strongly** recommend reading [doc/gitolite-and-ssh.mkd][doc9gas], which is a very detailed look at how gitolite uses ssh's features on the server side. Most people don't know ssh as well as they *think* they do; even if you don't have any problems right now, it's worth skimming over. @@ -51,7 +50,7 @@ Other resources: ---- - + ### (common) ssh asks for a password @@ -136,7 +135,7 @@ This is a quick checklist: ---- - + ### problems when using package, root, or non-root install methods @@ -163,7 +162,7 @@ first match, the second occurrence (which invokes gitolite) is ignored. You'll have to (create and) use a different keypair for gitolite access. - + ### problems when using the "from-client" install method @@ -191,7 +190,9 @@ key. All this applies *only* to the admin. Normal users will only have one key and do not need any of this. - + + + #### (sidebar) why two keys on client for the admin @@ -214,7 +215,7 @@ do not need any of this. > methods to install gitolite. Please see the [install doc][install] for > details. - + #### bypassing gitolite without intending to @@ -261,7 +262,7 @@ the repo and clones ok. But when you push, gitolite's **update hook** kicks in, and fails to run because some of the environment variables it is expecting are not present. - + #### basic ssh troubleshooting for the admin @@ -310,7 +311,7 @@ from scratch: That's a long sequence but it should work. - + ### windows issues @@ -326,13 +327,13 @@ how to resolve them and get things working, I'd be happy to credit you and include it, either directly here if it is short enough or just an external link, or in contrib/ if it's a longer piece of text. - + ### details Here's how it all hangs together. - + #### files on the server @@ -364,7 +365,7 @@ Here's how it all hangs together. argument `sitaram`. This is how gitolite is invoked, (and is told the user logging in is "sitaram"). - + #### files on client @@ -392,11 +393,11 @@ Here's how it all hangs together. non-default keypair (unlike command line ssh, which can be given the `-i ~/.ssh/sitaram` flag to do so). - + ### some other tips and tricks - + #### giving shell access to gitolite users @@ -415,7 +416,7 @@ or The first method is to be used if you used the **user-install** mode, while the second method is for the **system-install followed by user-setup** mode -(see doc/0-INSTALL.mkd, section on "install methods", for more on this) +(see doc/1-INSTALL.mkd, section on "install methods", for more on this) **IMPORTANT UPGRADE NOTE**: previous implementations of this feature were crap. There was no easy/elegant way to ensure that someone who had repo admin @@ -424,7 +425,7 @@ access would not manage to get himself shell access. Giving someone shell access requires that you should have shell access in the first place, so the simplest way is to enable it from the server side only. - + #### losing your admin key @@ -433,7 +434,7 @@ gitolite-admin repository with a fresh key, take a look at the `src/gl-dont-panic` program. You will need shell access to the server of course. Run it without arguments to get instructions. - + #### simulating ssh-copy-id @@ -458,11 +459,10 @@ typically) also must be `go-w`, but that needs root. And typically they're already set that way anyway. (Or if they're not, you've got bigger problems than gitolite install not working!)] -[doc0]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd -[doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/9-gitolite-and-ssh.mkd -[install]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd -[o3]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#installation_and_setup -[fc]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#from_client_method_install_from_the_client_to_the_server -[urls]: http://github.com/sitaramc/gitolite/blob/pu/doc/0-INSTALL.mkd#URLs_for_gitolite_managed_repos +[doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite-and-ssh.mkd +[install]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd +[o3]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd#methods +[fc]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd#fc +[urls]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd#URLs_for_gitolite_managed_repos [repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd -[doc7]: http://github.com/sitaramc/gitolite/blob/pu/doc/7-install-transcript.mkd +[transcript]: http://github.com/sitaramc/gitolite/blob/pu/doc/install-transcript.mkd diff --git a/doc/9-uninstall.mkd b/doc/uninstall.mkd similarity index 100% rename from doc/9-uninstall.mkd rename to doc/uninstall.mkd diff --git a/doc/4-wildcard-repositories.mkd b/doc/wildcard-repositories.mkd similarity index 86% rename from doc/4-wildcard-repositories.mkd rename to doc/wildcard-repositories.mkd index e9a506b..974455e 100644 --- a/doc/4-wildcard-repositories.mkd +++ b/doc/wildcard-repositories.mkd @@ -19,17 +19,17 @@ workarounds I may not have the time to code it right away. In this document: - * rc file setting required - * wildcard repos - * wildcard repos with creator name in them - * wildcard repos without creator name in them - * side-note: line-anchored regexes - * contrast with refexes - * handing out rights to wildcard-matched repos - * setting a gitweb description for a wildcard-matched repo - * reporting - * other issues and discussion - * how it actually works + * rc file setting required + * wildcard repos + * wildcard repos with creator name in them + * wildcard repos without creator name in them + * side-note: line-anchored regexes + * contrast with refexes + * handing out rights to wildcard-matched repos + * setting a gitweb description for a wildcard-matched repo + * reporting + * other issues and discussion + * how it actually works ---- @@ -37,7 +37,7 @@ This document is mostly "by example". ---- - + ### rc file setting required @@ -45,7 +45,7 @@ This feature requires that you set `$GL_WILDREPOS` to "1" in `~/.gitolite.rc` on the server. Please search for that variable and see comments around it in `conf/example.gitolite.rc` for more information on this. - + ### wildcard repos @@ -53,7 +53,7 @@ Which of these alternatives you choose depends on your needs, and the social aspects of your environment. The first one is a little more rigid, making it harder to make mistakes, and the second is more flexible and trusting. - + #### wildcard repos with creator name in them @@ -79,7 +79,7 @@ new repo, as user "u4" (a student): Notice the *two* empty repo inits, and the order in which they occur ;-) - + #### wildcard repos without creator name in them @@ -106,7 +106,7 @@ and have a TA create the repos in advance. In either case, they could then use the `setperms` feature to specify which users are "READERS" and which are "WRITERS". See later for details. - + ### side-note: line-anchored regexes @@ -123,7 +123,7 @@ But you may be surprised to find that it does not match even `^assignments/S[0-9]+/A[0-9]+$` -- notice the line beginning and ending metacharacters. - + #### contrast with refexes @@ -135,7 +135,7 @@ if no one will actually push such a branch! You can anchor both sides if you really care, by using `master$` instead of `master`, but that is *not* the default for refexes. - + ### handing out rights to wildcard-matched repos @@ -182,23 +182,21 @@ The following points are important: * whoever you specify as "R" will match the special user READERS. "RW" will match WRITERS. - + ### setting a gitweb description for a wildcard-matched repo Similar to the getperms/setperms commands, there are the getdesc/setdesc commands, thanks to Teemu. - + ### reporting In order to see what repositories were created from a wildcard, use the "expand" command, described briefly in [doc/report-output.mkd][repout]. -[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd - - + ### other issues and discussion @@ -220,7 +218,7 @@ In order to see what repositories were created from a wildcard, use the of the time, it won't be difficult; the fixed prefix will usually be different anyway so there won't be overlaps. - + ### how it actually works @@ -295,6 +293,4 @@ I hope that helps. Enjoy, and please use with care. This is pretty powerful stuff. As they say: if you break it, you get to keep both pieces :) -[jwzq]: http://regex.info/blog/2006-09-15/247 - -[av]: http://en.wikipedia.org/wiki/Autovivification +[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd From 7b633049be71642da97aae1ae8fc5995a3a1a888 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 4 Sep 2010 15:03:06 +0530 Subject: [PATCH 466/850] refactored and lifted out the line parse part from inside parse_conf_file adapted from code by kpfleming@digium.com. I basically cherry-picked the top commit on "pu-work" (30068d1) on his fork at github, and made some minor fixups to it --- src/gl-compile-conf | 346 +++++++++++++++++++++++--------------------- 1 file changed, 181 insertions(+), 165 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 13897f6..efc4bc5 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -163,6 +163,180 @@ sub check_fragment_repo_disallowed return 1; } +sub parse_conf_line +{ + my ($line, $fragment, $repos_p, $ignored_p) = @_; + + # user or repo groups + if ($line =~ /^(@\S+) = (.*)/) + { + die "$ABRT defining groups is not allowed inside fragments\n" + if $GL_BIG_CONFIG and $fragment ne 'master'; + # store the members of each group as hash key. Keep track of when + # the group was *first* created by using $fragment as the *value* + do { $groups{$1}{$_} ||= $fragment } for ( expand_list( split(' ', $2) ) ); + die "$ABRT bad group $1\n" unless $1 =~ $REPONAME_PATT; + } + # repo(s) + elsif ($line =~ /^repo (.*)/) + { + # grab the list... + @{ $repos_p } = split ' ', $1; + unless (@{ $repos_p } == 1 and $repos_p->[0] eq '@all') { + # ...expand groups in the default case + @{ $repos_p } = expand_list ( @{ $repos_p } ) unless $GL_BIG_CONFIG; + # ...sanity check + for (@{ $repos_p }) { + die "$ABRT bad reponame $_\n" + if ($GL_WILDREPOS and $_ !~ $REPOPATT_PATT); + die "$ABRT bad reponame $_ or you forgot to set \$GL_WILDREPOS\n" + if (not $GL_WILDREPOS and $_ !~ $REPONAME_PATT); + } + } + s/\bCREAT[EO]R\b/\$creator/g for @{ $repos_p }; + } + # actual permission line + elsif ($line =~ /^(-|C|R|RW\+?(?:C?D?|D?C?)) (.* )?= (.+)/) + { + my $perms = $1; + my @refs; @refs = split( ' ', $2 ) if $2; + @refs = expand_list ( @refs ); + my @users = split ' ', $3; + die "$ABRT \$GL_WILDREPOS is not set, you cant use 'C' in config\n" if $perms eq 'C' and not $GL_WILDREPOS; + + # if no ref is given, this PERM applies to all refs + @refs = qw(refs/.*) unless @refs; + # deprecation warning + map { warn "WARNING: old syntax 'PATH/' found; please use new syntax 'NAME/'\n" if s(^PATH/)(NAME/) } @refs; + # fully qualify refs that dont start with "refs/" or "NAME/"; + # prefix them with "refs/heads/" + @refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs; + @refs = map { s(/USER/)(/\$gl_user/); $_ } @refs; + + # expand the user list, unless it is just "@all" + @users = expand_list ( @users ) + unless ($GL_BIG_CONFIG or (@users == 1 and $users[0] eq '@all')); + do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; + + s/\bCREAT[EO]R\b/~\$creator/g for @users; + s/\bREADERS\b/\$readers/g for @users; + s/\bWRITERS\b/\$writers/g for @users; + + # ok, we can finally populate the %repos hash + for my $repo (@{ $repos_p }) # each repo in the current stanza + { + # if we're processing a delegated config file (not the master + # config), we need to prevent attempts by that admin to obtain + # rights on stuff outside his domain + + # trying to set access for $repo (='foo')... + if (check_fragment_repo_disallowed( $fragment, $repo )) + { + $ignored_p->{$fragment}{$repo} = 1; + next; + } + for my $user (@users) + { + # lint check, to catch pubkey/username typos + if ($user =~ /^@/ and $user ne '@all') { + # this is a usergroup, not a normal user; happens with GL_BIG_CONFIG + if (exists $groups{$user}) { + $user_list{$_}++ for keys %{ $groups{$user} }; + } + } else { + $user_list{$user}++; + } + + # for 1st level check (see faq/tips doc) + $repos{$repo}{C}{$user} = 1, next if $perms eq 'C'; + $repos{$repo}{R}{$user} = 1 if $perms =~ /R/; + $repos{$repo}{W}{$user} = 1 if $perms =~ /W|D/; + + # if the user specified even a single 'D' anywhere, make + # that fact easy to find; this changes the meaning of RW+ + # to no longer permit deletes (see update hook) + $repos{$repo}{DELETE_IS_D} = 1 if $perms =~ /D/; + $repos{$repo}{CREATE_IS_C} = 1 if $perms =~ /RW.*C/; + + # for 2nd level check, store each "ref, perms" pair in order + for my $ref (@refs) + { + # checking NAME based restrictions is expensive for + # the update hook (see the changes to src/hooks/update + # in this commit for why) so we would *very* much like + # to avoid doing it for the large majority of repos + # that do *not* use NAME limits. Setting a flag that + # can be checked right away will help us do that + $repos{$repo}{NAME_LIMITS} = 1 if $ref =~ /^NAME\//; + my $p_user = $user; $p_user =~ s/(creator|readers|writers)$/$1 - wild/; + push @{ $repos{$repo}{$p_user} }, [ $rule_seq++, $ref, $perms ] + unless $rurp_seen{$repo}{$p_user}{$ref}{$perms}++; + } + } + } + } + # configuration + elsif ($line =~ /^config (.+) = ?(.*)/) + { + my ($key, $value) = ($1, $2); + my @validkeys = split(' ', ($GL_GITCONFIG_KEYS || '') ); + my @matched = grep { $key =~ /^$_$/ } @validkeys; + die "$ABRT git config $key not allowed\n" if (@matched < 1); + for my $repo (@{ $repos_p }) # each repo in the current stanza + { + $repo_config{$repo}{$key} = $value; + # no problem if it's a plain repo (non-pattern, non-groupname) + # OR wild configs are allowed + unless ( ($repo =~ $REPONAME_PATT and $repo !~ /^@/) or $GL_GITCONFIG_WILD) { + my @r = ($repo); # single wildpatt + @r = sort keys %{ $groups{$repo} } if $groups{$repo}; # or a group; get its members + do { + print STDERR "$WARN git config set for $_ but \$GL_GITCONFIG_WILD not set\n" unless $_ =~ $REPONAME_PATT + } for @r; + } + } + } + # include + elsif ($line =~ /^include "(.+)"/) + { + my $file = $1; + $file = "$GL_ADMINDIR/conf/$file" unless $file =~ /^\//; + die "$WARN $fragment attempting to include configuration\n" if $fragment ne 'master'; + die "$ABRT included file not found: '$file'\n" unless -f $file; + + parse_conf_file( $file, $fragment ); + } + # very simple syntax for the gitweb description of repo; one of: + # reponame = "some description string" + # reponame "owner name" = "some description string" + elsif ($line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/) + { + my ($repo, $owner, $desc) = ($1, $2, $3); + die "$ABRT bad repo name $repo\n" unless $repo =~ $REPONAME_PATT; + die "$WARN $fragment attempting to set description for $repo\n" if check_fragment_repo_disallowed( $fragment, $repo ); + $desc{"$repo.git"} = $desc; + $owner{"$repo.git"} = $owner || ''; + } + else + { + die "$ABRT can't make head or tail of '$line'\n"; + } +} + +sub cleanup_conf_line +{ + my ($line) = @_; + + # kill comments, but take care of "#" inside *simple* strings + $line =~ s/^((".*?"|[^#"])*)#.*/$1/; + # normalise whitespace; keeps later regexes very simple + $line =~ s/=/ = /; + $line =~ s/\s+/ /g; + $line =~ s/^ //; + $line =~ s/ $//; + return $line; +} + sub parse_conf_file { my ($conffile, $fragment) = @_; @@ -178,178 +352,20 @@ sub parse_conf_file # the syntax is fairly simple, so we parse it inline my @repos; + my $line; while (<$conf_fh>) { - # kill comments, but take care of "#" inside *simple* strings - s/^((".*?"|[^#"])*)#.*/$1/; - # normalise whitespace; keeps later regexes very simple - s/=/ = /; - s/\s+/ /g; - s/^ //; - s/ $//; - # and blank lines - next unless /\S/; + $line = cleanup_conf_line($_); + # skip blank lines + next unless $line =~ /\S/; - # user or repo groups - if (/^(@\S+) = (.*)/) - { - die "$ABRT defining groups is not allowed inside fragments\n" - if $GL_BIG_CONFIG and $fragment ne 'master'; - # store the members of each group as hash key. Keep track of when - # the group was *first* created by using $fragment as the *value* - do { $groups{$1}{$_} ||= $fragment } for ( expand_list( split(' ', $2) ) ); - die "$ABRT bad group $1\n" unless $1 =~ $REPONAME_PATT; - } - # repo(s) - elsif (/^repo (.*)/) - { - # grab the list... - @repos = split ' ', $1; - unless (@repos == 1 and $repos[0] eq '@all') { - # ...expand groups in the default case - @repos = expand_list ( @repos ) unless $GL_BIG_CONFIG; - # ...sanity check - for (@repos) { - die "$ABRT bad reponame $_\n" - if ($GL_WILDREPOS and $_ !~ $REPOPATT_PATT); - die "$ABRT bad reponame $_ or you forgot to set \$GL_WILDREPOS\n" - if (not $GL_WILDREPOS and $_ !~ $REPONAME_PATT); - } - } - s/\bCREAT[EO]R\b/\$creator/g for @repos; - } - # actual permission line - elsif (/^(-|C|R|RW\+?(?:C?D?|D?C?)) (.* )?= (.+)/) - { - my $perms = $1; - my @refs; @refs = split(' ', $2) if $2; - @refs = expand_list ( @refs ); - my @users = split ' ', $3; - die "$ABRT \$GL_WILDREPOS is not set, you cant use 'C' in config\n" if $perms eq 'C' and not $GL_WILDREPOS; - - # if no ref is given, this PERM applies to all refs - @refs = qw(refs/.*) unless @refs; - # deprecation warning - map { warn "WARNING: old syntax 'PATH/' found; please use new syntax 'NAME/'\n" if s(^PATH/)(NAME/) } @refs; - # fully qualify refs that dont start with "refs/" or "NAME/"; - # prefix them with "refs/heads/" - @refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs; - @refs = map { s(/USER/)(/\$gl_user/); $_ } @refs; - - # expand the user list, unless it is just "@all" - @users = expand_list ( @users ) - unless ($GL_BIG_CONFIG or (@users == 1 and $users[0] eq '@all')); - do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users; - - s/\bCREAT[EO]R\b/~\$creator/g for @users; - s/\bREADERS\b/\$readers/g for @users; - s/\bWRITERS\b/\$writers/g for @users; - - # ok, we can finally populate the %repos hash - for my $repo (@repos) # each repo in the current stanza - { - # if we're processing a delegated config file (not the master - # config), we need to prevent attempts by that admin to obtain - # rights on stuff outside his domain - - # trying to set access for $repo (='foo')... - if (check_fragment_repo_disallowed($fragment, $repo)) - { - $ignored{$fragment}{$repo} = 1; - next; - } - for my $user (@users) - { - # lint check, to catch pubkey/username typos - if ($user =~ /^@/ and $user ne '@all') { - # this is a usergroup, not a normal user; happens with GL_BIG_CONFIG - if (exists $groups{$user}) { - $user_list{$_}++ for keys %{ $groups{$user} }; - } - } else { - $user_list{$user}++; - } - - # for 1st level check (see faq/tips doc) - $repos{$repo}{C}{$user} = 1, next if $perms eq 'C'; - $repos{$repo}{R}{$user} = 1 if $perms =~ /R/; - $repos{$repo}{W}{$user} = 1 if $perms =~ /W|D/; - - # if the user specified even a single 'D' anywhere, make - # that fact easy to find; this changes the meaning of RW+ - # to no longer permit deletes (see update hook) - $repos{$repo}{DELETE_IS_D} = 1 if $perms =~ /D/; - $repos{$repo}{CREATE_IS_C} = 1 if $perms =~ /RW.*C/; - - # for 2nd level check, store each "ref, perms" pair in order - for my $ref (@refs) - { - # checking NAME based restrictions is expensive for - # the update hook (see the changes to src/hooks/update - # in this commit for why) so we would *very* much like - # to avoid doing it for the large majority of repos - # that do *not* use NAME limits. Setting a flag that - # can be checked right away will help us do that - $repos{$repo}{NAME_LIMITS} = 1 if $ref =~ /^NAME\//; - my $p_user = $user; $p_user =~ s/(creator|readers|writers)$/$1 - wild/; - push @{ $repos{$repo}{$p_user} }, [ $rule_seq++, $ref, $perms ] - unless $rurp_seen{$repo}{$p_user}{$ref}{$perms}++; - } - } - } - } - # configuration - elsif (/^config (.+) = ?(.*)/) - { - my ($key, $value) = ($1, $2); - my @validkeys = split (' ', ($GL_GITCONFIG_KEYS || '')); - my @matched = grep { $key =~ /^$_$/ } @validkeys; - die "$ABRT git config $key not allowed\n" if (@matched < 1); - for my $repo (@repos) # each repo in the current stanza - { - $repo_config{$repo}{$key} = $value; - # no problem if it's a plain repo (non-pattern, non-groupname) - # OR wild configs are allowed - unless ( ($repo =~ $REPONAME_PATT and $repo !~ /^@/) or $GL_GITCONFIG_WILD) { - my @r = ($repo); # single wildpatt - @r = sort keys %{ $groups{$repo} } if $groups{$repo}; # or a group; get its members - do { - print STDERR "$WARN git config set for $_ but \$GL_GITCONFIG_WILD not set\n" unless $_ =~ $REPONAME_PATT - } for @r; - } - } - } - # include - elsif (/^include "(.+)"/) - { - my $file = $1; - $file = "$GL_ADMINDIR/conf/$file" unless $file =~ /^\//; - die "$WARN $fragment attempting to include configuration\n" if $fragment ne 'master'; - die "$ABRT included file not found: '$file'\n" unless -f $file; - - parse_conf_file($file, $fragment); - } - # very simple syntax for the gitweb description of repo; one of: - # reponame = "some description string" - # reponame "owner name" = "some description string" - elsif (/^(\S+)(?: "(.*?)")? = "(.*)"$/) - { - my ($repo, $owner, $desc) = ($1, $2, $3); - die "$ABRT bad repo name $repo\n" unless $repo =~ $REPONAME_PATT; - die "$WARN $fragment attempting to set description for $repo\n" if check_fragment_repo_disallowed($fragment, $repo); - $desc{"$repo.git"} = $desc; - $owner{"$repo.git"} = $owner || ''; - } - else - { - die "$ABRT can't make head or tail of '$_'\n"; - } + parse_conf_line( $line, $fragment, \@repos, \%ignored ); } for my $ig (sort keys %ignored) { warn "\n\t\t***** WARNING *****\n" . - "\t$ig.conf attempting to set access for " . - join (", ", sort keys %{ $ignored{$ig} }) . "\n"; + "\t$ig.conf attempting to set access for " . + join (", ", sort keys %{ $ignored{$ig} }) . "\n"; } } From 6b93678ac62f82c51919ee6b32ca1fea57e7b47e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 4 Sep 2010 18:08:29 +0530 Subject: [PATCH 467/850] (minor) sshkeys-lint prints correct path to run in instructions --- src/sshkeys-lint | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sshkeys-lint b/src/sshkeys-lint index acb8d72..a283bfe 100755 --- a/src/sshkeys-lint +++ b/src/sshkeys-lint @@ -3,6 +3,9 @@ use strict; our (%users, %linenos); +my $thisbin = $0; +$thisbin = "$ENV{PWD}/$thisbin" unless $thisbin =~ /^\//; + &usage unless $ARGV[0] and -f $ARGV[0]; my @authlines = &filelines($ARGV[0]); my $lineno = 0; @@ -88,7 +91,7 @@ On your *client*: - cd to the ~/.ssh directory (which contains all the pub keys this client can use) - - run "$0 /tmp/foo" + - run "$thisbin /tmp/foo" Note: people who have so many keypairs they keep them in *sub*-directories of ~/.ssh [you know who you are ;-)] can figure it out themselves; you clearly From a9e9f98a7e994bfb127e3693896b001bed76df81 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 5 Sep 2010 18:34:30 +0530 Subject: [PATCH 468/850] (minor) fixup to post-update hook --- hooks/gitolite-admin/post-update | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/gitolite-admin/post-update b/hooks/gitolite-admin/post-update index 0d1ba99..b390827 100755 --- a/hooks/gitolite-admin/post-update +++ b/hooks/gitolite-admin/post-update @@ -19,7 +19,7 @@ $GL_BINDIR/gl-compile-conf cd $od -ADMIN_POST_UPDATE_CHAINS_TO=` cd;perl -e 'do ".gitolite.rc"; print $ADMIN_POST_UPDATE_CHAINS_TO'` +ADMIN_POST_UPDATE_CHAINS_TO=` cd $HOME;perl -e 'do ".gitolite.rc"; print $ADMIN_POST_UPDATE_CHAINS_TO'` [ -n "$ADMIN_POST_UPDATE_CHAINS_TO" ] || ADMIN_POST_UPDATE_CHAINS_TO=hooks/post-update.secondary if [ -f $ADMIN_POST_UPDATE_CHAINS_TO ] || [ -L $ADMIN_POST_UPDATE_CHAINS_TO ] From 52e0ed34882ba22edd857bdefc0d57afe8a98bb2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 5 Sep 2010 18:43:21 +0530 Subject: [PATCH 469/850] (http) auth: handle REQUEST_URI and friends TODO: if the verb doesn't actually contain "git-receive-pack", I am assuming it is some sort of read. The list in services[] in http-backend.c does not seem to look like any other verb is a "write"; need to check this with someone. For normal git commands: - PATH_INFO gives you the repo name - REQUEST_URI gives you the verb - we construct a fake SSH_ORIGINAL_COMMAND so the rest of the processing does not have to change For our special commands: - PATH_INFO is actually the verb - QUERY_STRING has the parameters - we again fake out the SSH_ORIGINAL_COMMAND - we print the extra HTTP headers in anticipation of the actual output Either way, we also fake out the SSH_CONNECTION so that the IP address can get logged ok And of course REMOTE_USER is now the incoming userid Finally, at the end, we exec GIT_HTTP_BACKEND instead of the normal one --- src/gitolite.pm | 16 +++++++++ src/gl-auth-command | 85 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 78d166f..cbd6180 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -92,6 +92,22 @@ sub dbg { } } +my $http_headers_printed = 0; +sub print_http_headers { + my($code, $text) = @_; + + return if $http_headers_printed++; + $code ||= 200; + $text ||= "OK - gitolite"; + + $|++; + print "Status: $code $text\r\n"; + print "Expires: Fri, 01 Jan 1980 00:00:00 GMT\r\n"; + print "Pragma: no-cache\r\n"; + print "Cache-Control: no-cache, max-age=0, must-revalidate\r\n"; + print "\r\n"; +} + sub get_logfilename { # this sub has a wee little side-effect; it sets $ENV{GL_TS} my($template) = shift; diff --git a/src/gl-auth-command b/src/gl-auth-command index 958eb5b..a95fbc3 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -3,20 +3,29 @@ use strict; use warnings; -# === auth-command === -# the command that GL users actually run - -# part of the gitolite (GL) suite - -# how run: via sshd, being listed in "command=" in ssh authkeys -# when: every login by a GL user -# input: $1 is GL username, plus $SSH_ORIGINAL_COMMAND -# output: -# security: - -# robustness: - -# other notes: +# ---------------------------------------------------------------------------- +# you: what's the invocation? +# me: Hail, O Lord Ganesha, destroyer of obsta... +# you: err hmm not *that* sort of invocation... I meant how does this program +# get invoked? +# me: oh hehe , ok here we go... +# +# ssh mode +# - started by sshd +# - one argument, the "user" name +# - one env var, SSH_ORIGINAL_COMMAND, containing the command +# - command typically: git-(receive|upload)-pack 'reponame(.git)?' +# - special gitolite commands: info, expand, (get|set)(perms|desc) +# - special non-gitolite commands: rsync, svnserve, htpasswd +# - other commands: anything in $GL_ADC_PATH if defined (see rc file) +# +# (smart) http mode +# - started by apache (httpd) +# - no arguments +# - REQUEST_URI contains verb and repo, REMOTE_USER contains username +# - REQUEST_URI looks like /path/reponame.git/(info/refs\?service=)?git-(receive|upload)-pack +# - no special processing commands currently handled +# ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # common definitions @@ -67,12 +76,43 @@ if (@ARGV and $ARGV[0] eq '-s') { shift; } -# no (more) arguments given? default user is $USER (fedorahosted works like -# this, and it is harmless for others) -@ARGV = ($ENV{USER}) unless @ARGV; +# ---------------------------------------------------------------------------- +# set up SSH_ORIGINAL_COMMAND and SSH_CONNECTION in http mode +# ---------------------------------------------------------------------------- -# first, fix the biggest gripe I have with gitosis, a 1-line change -my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere! +# fake out SSH_ORIGINAL_COMMAND and SSH_CONNECTION so the rest of the code +# stays the same (except the exec at the end). + +my $user; +if ($ENV{REQUEST_URI}) { + # these patterns indicate normal git usage; see "services[]" in + # http-backend.c for how I got that. Also note that "info" is overloaded; + # git uses "info/refs...", while gitolite uses "info" or "info?...". So + # there's a "/" after info in the list below + if ($ENV{PATH_INFO} =~ m(/(HEAD$|info/refs$|objects/|git-(?:upload|receive)-pack$))) { + my ($repo) = ($ENV{PATH_INFO} =~ m(^/(.*)\.git(?:/|$))); + my ($verb) = ($ENV{REQUEST_URI} =~ m((git-(?:receive|upload)-pack))); + print STDERR "(gitolite) no verb found in $ENV{REQUEST_URI}\n" unless $verb; + $verb ||= 'git-upload-pack'; + $ENV{SSH_ORIGINAL_COMMAND} = "$verb '$repo'"; + } else { + # this is one of our custom commands; could be anything really, + # because of the adc feature + my ($verb) = ($ENV{PATH_INFO} =~ m(^/(\S+))); + my $args = $ENV{QUERY_STRING}; + $args =~ s/\+/ /g; + $ENV{SSH_ORIGINAL_COMMAND} = $verb; + $ENV{SSH_ORIGINAL_COMMAND} .= " $args" if $args; + &print_http_headers(); # in preparation for the eventual output! + } + $ENV{SSH_CONNECTION} = "$ENV{REMOTE_ADDR} $ENV{REMOTE_PORT} $ENV{SERVER_ADDR} $ENV{SERVER_PORT}"; + $user = $ENV{GL_USER} = $ENV{REMOTE_USER}; +} else { + # no (more) arguments given in ssh mode? default user is $USER + # (fedorahosted works like this, and it is harmless for others) + @ARGV = ($ENV{USER}) unless @ARGV; + $user=$ENV{GL_USER}=shift; +} # if there are any more arguments, they're a list of group names that the user # is a member of @@ -212,6 +252,13 @@ die "$aa access for $repo DENIED to $user\n" unless $perm =~ /$aa/; # over to git now # ---------------------------------------------------------------------------- +if ($ENV{REQUEST_URI}) { + &log_it($ENV{REQUEST_URI}); + exec $ENV{GIT_HTTP_BACKEND}; + # the GIT_HTTP_BACKEND env var should be set either by the rc file, or as + # a SetEnv in the apache config somewhere +} + &log_it(); $repo = "'$REPO_BASE/$repo.git'"; From f4e011226abebfbfba4557297732c81fd1c3e675 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 5 Sep 2010 18:47:10 +0530 Subject: [PATCH 470/850] (http) issues of $HOME, startup (birth!), and death - deal with issues of HOME not being available... - "where_is_rc" finally has a purpose; see comment block before function --- src/gitolite.pm | 55 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index cbd6180..863683b 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -209,33 +209,50 @@ sub collect_repo_patts # ---------------------------------------------------------------------------- -# where is the rc file hiding? +# birth and death registration ;-) # ---------------------------------------------------------------------------- +# background + +# till now, the rc file was in one fixed place: .gitolite.rc in $HOME of the +# user hosting the gitolite repos. This was fine, because gitolite is all +# about empowering non-root users :-) + +# but in smart http mode, running under "apache", you should actually use +# $GITOLITE_HTTP_HOME instead of $HOME (in fact $HOME may not even be +# defined). However, the dependency on $HOME is so pervasive that we'd best +# just set it here and be done. We also set $ENV{GL_RC} to point to the rc +# file + +# every gitolite program ends up calling this anyway, so that's birth + +# the second thing we need to do is handle death a little better. A plain +# "die" was fine for ssh but http has all that extra gunk it needs. So we +# need to, in effect, create a "death handler". + +# the name of the sub, however, is a holdover from when that was the sole +# purpose. I suck at function names anyway... + sub where_is_rc { - # till now, the rc file was in one fixed place: .gitolite.rc in $HOME of - # the user hosting the gitolite repos. This was fine, because gitolite is - # all about empowering non-root users :-) + die "I need either HOME or GITOLITE_HTTP_HOME env vars set\n" unless $ENV{GITOLITE_HTTP_HOME} or $ENV{HOME}; + if ($ENV{GITOLITE_HTTP_HOME}) { + # smart http mode; GITOLITE_HTTP_HOME becomes our HOME + $ENV{HOME} = $ENV{GITOLITE_HTTP_HOME}; - # then we wanted to make a debian package out of it (thank you, Rhonda!) - # which means (a) it's going to be installed by root anyway and (b) any - # config files have to be in /etc/ - - # the only way to resolve this in a backward compat way is to look for the - # $HOME one, and if you don't find it look for the /etc one - - # this common routine does that, setting an env var for the first one it - # finds + $SIG{__DIE__} = sub { + my $msg = shift; chomp($msg); + &print_http_headers(500, "error - gitolite"); + print "$msg\r\n"; + print STDERR "$msg\n"; + exit 0; # if it's ok for die_webcgi in git.git/http-backend.c, it's ok for me ;-) + } + } return if $ENV{GL_RC}; - for my $glrc ( $ENV{HOME} . "/.gitolite.rc", "/etc/gitolite/gitolite.rc" ) { - if (-f $glrc) { - $ENV{GL_RC} = $glrc; - return; - } - } + my $glrc = $ENV{HOME} . "/.gitolite.rc"; + $ENV{GL_RC} = $glrc if (-f $glrc); } # ---------------------------------------------------------------------------- From ec329ca13f0fb5ab6660a62b0eac5751b9fc0ede Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 5 Sep 2010 18:48:29 +0530 Subject: [PATCH 471/850] (http) gl-setup changes... - only admin name needed, not pubkey file - setup HOME from GITOLITE_HTTP_HOME --- src/gl-setup | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/gl-setup b/src/gl-setup index d19b6b2..f0a3530 100755 --- a/src/gl-setup +++ b/src/gl-setup @@ -21,14 +21,21 @@ GL_PACKAGE_CONF=/tmp/share/gitolite/conf die() { echo "$@"; exit 1; } -pubkey_file=$1 -admin_name= -if [ -n "$pubkey_file" ] + +if [ -n "$GITOLITE_HTTP_HOME" ] then - echo $pubkey_file | grep '.pub$' >/dev/null || die "$pubkey_file must end in .pub" - [ -f $pubkey_file ] || die "cant find $pubkey_file" - admin_name=` basename $pubkey_file .pub` - echo $admin_name | grep '@' >/dev/null && die "please don't use '@' in the initial admin name" + HOME=$GITOLITE_HTTP_HOME + admin_name=$1 +else + pubkey_file=$1 + admin_name= + if [ -n "$pubkey_file" ] + then + echo $pubkey_file | grep '.pub$' >/dev/null || die "$pubkey_file must end in .pub" + [ -f $pubkey_file ] || die "cant find $pubkey_file" + admin_name=` basename $pubkey_file .pub` + echo $admin_name | grep '@' >/dev/null && die "please don't use '@' in the initial admin name" + fi fi if [ -f $HOME/.gitolite.rc ] @@ -49,7 +56,9 @@ then fi rm -f .newvars .oldvars .diffvars else - [ -n "$pubkey_file" ] || die "looks like first run -- I need a pubkey file" + [ -n "$GITOLITE_HTTP_HOME" ] || [ -n "$pubkey_file" ] || die "looks like first run -- I need a pubkey file" + [ -z "$GITOLITE_HTTP_HOME" ] || [ -n "$admin_name" ] || die "looks like first run -- I need an admin name" + cp $GL_PACKAGE_CONF/example.gitolite.rc $HOME/.gitolite.rc fi From cc8ccab92458777f7a32e1c5921fbf2f1b2c39cf Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 5 Sep 2010 18:48:38 +0530 Subject: [PATCH 472/850] (http) gitolite without ssh? smart http support is here! As usual there's more documentation than code. Unlike usual, however, this isn't completely tested. Please read the documentation for details of what works, what doesn't, what has been tested, what hasn't, and so on. --- doc/CHANGELOG | 8 +++ doc/http-backend.mkd | 152 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 doc/http-backend.mkd diff --git a/doc/CHANGELOG b/doc/CHANGELOG index b870cf8..51459a2 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,6 +2,14 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] + - (BIG ONE!) SMART HTTP SUPPORT! + + - @all can now include gitweb/daemon also (default, not include) + - allow @groups in setperms + - gitweb/daemon now work within setperms + + - log elapsed time (optional) + - more than one wildcard may match a repo, plus it can also be matched by a normal repo line diff --git a/doc/http-backend.mkd b/doc/http-backend.mkd new file mode 100644 index 0000000..fbc7300 --- /dev/null +++ b/doc/http-backend.mkd @@ -0,0 +1,152 @@ +# how to setup gitolite to use smart http mode + +In this document: + + * WARNINGS, plus stuff I need help with + * additional requirements + * detailed instructions + * install gitolite under "apache" + * setup the http-backend + * usage + +---- + + + +### WARNINGS, plus stuff I need help with + + * I have NOT converted the test suite to use this mode. Volunteers to + convert it to http access are welcome :-) + + * I have no idea how to handle the password issue other than creating a + `~/.netrc` file and making it `chmod 600`. Anyway, http based access is + inherently less secure than pubkeys so not much point worrying about it. + + * messages from first level errors (where gitolite's 'gl-auth-command' bombs + out without letting git even get control, due to inadequate access) don't + come to the user. The git client is swallowing up a perfectly good error + message -- you can see it if you manually retry like this: + + $ curl http://bob:bob@127.0.0.1/git/foo/alice/a1.git/info/refs?service=git-receive-pack + W access for foo/alice/a1 DENIED to bob + + What's worse, when this failure happens, the git client retries a few + other things, eventually ending up doing a `PROPFIND` webdav attempt so + you get a DAV error of some kind. + + * I have not tested any of the ancillary standalone programs (like + gl-dont-panic) in this mode. They're most likely going to crash and burn + because `$HOME` is not defined or in the wrong place; manually set + `HOME=$GITOLITE_HTTP_HOME` and hope for the best. Luckily most of them + have to do with sshkeys so this may not matter. YMMV. + + * tested on stock Fedora 13; if you test on other environments please let me + know how it worked out and if we need to adjust this document + + * tested only http, not https; if you do, and it works, please tell me + + * have not tried making repos available to both ssh *and* http mode clients; + (I'd guess it ought to work fine if the "apache" user was made login-able + and given a proper $HOME and `~/.ssh/authorized_keys` and all that). If + anyone has the energy to try that please let me know how that went. + + + +### additional requirements + + * requires `GIT_PROJECT_ROOT` (see "man git-http-backend" for what this is) + set explicitly (i.e., it is no longer optional). Please set it to some + place outside apache's `DOCUMENT_ROOT`. + + + +### detailed instructions + +I assume you've installed apache 2.x and git on the server. + +I assume your httpd runs under the "apache" userid; adjust instructions below +if it does not. Similarly for "/var/www" and other file names/locations. + + + +#### install gitolite under "apache" + + * follow the "non-root" method, but since you can't even "su - apache", make + the following variations when doing this as root: + + * `cd ~apache` first; this is `/var/www` on Fedora 13 + + * do this in the shell + + mkdir gitolite-home + export GITOLITE_HTTP_HOME + GITOLITE_HTTP_HOME=/var/www/gitolite-home + PATH=$PATH:$GITOLITE_HTTP_HOME/bin + + * now run the first 3 install steps for "non-root" method (clone, mkdir, + and gl-system-install), but **substitute `GITOLITE_HTTP_HOME` in place of + `HOME`** in the mkdir and gl-system-install steps. + + **Do NOT run the gl-setup step yet**. + + * after the gl-system-install step, add these to the **top** of + /var/www/gitolite-home/share/gitolite/conf/example.gitolite.rc + + $ENV{GIT_HTTP_BACKEND} = "/usr/libexec/git-core/git-http-backend"; + # or wherever you have that file; not NO trailing slash + $ENV{PATH} .= ":$ENV{GITOLITE_HTTP_HOME}/bin"; + # note the ".=" here, not "=" + + * run gl-setup with the name of your admin user + + gl-setup sitaram + + * IMPORTANT: fix up ownerships + + chown -R apache.apache $GITOLITE_HTTP_HOME + + + +#### setup the http-backend + + * when you setup the apache config according to "man git-http-backend", + change these two as below (please note the trailing slash on the + ScriptAlias line): + + SetEnv GIT_PROJECT_ROOT /var/www/gitolite-home/repositories + ScriptAlias /git/ /var/www/gitolite-home/bin/gl-auth-command/ + + You also need this new variable: + + SetEnv GITOLITE_HTTP_HOME /var/www/gitolite-home + +And that's it... you're done for the setup! + + + +### usage + +Git URLs look like `http://user:password@server/git/reponame.git`. + +The custom commands, like "info", "expand" should be handled as follows. The +command name will come just after the `/git/`, followed by a `?`, followed by +the arguments, with `+` representing a space. Here are some examples: + + # ssh git@server info + curl http://user:password@server/git/info + # ssh git@server info repopatt + curl http://user:password@server/git/info?repopatt + # ssh git@server info repopatt user1 user2 + curl http://user:password@server/git/info?repopatt+user1+user2 + +It gets even more interesting for the `setperms` command, which expects STDIN. +I didn't want to get too much into the code here, so I found that the +following works and I'm leaving it at that: + + (echo R user1 user2; echo RW user3 user4) | + curl --data-binary @- http://user:password@server/git/setperms?reponame.git + +With a few nice shell aliases, you won't even notice the horrible convolutions +here ;-) + +Enjoy! From 370135d2303e05d33e263ddece1ca58429ba7303 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 5 Sep 2010 19:06:18 +0530 Subject: [PATCH 473/850] (http) better guess at reponame --- src/gl-auth-command | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index a95fbc3..10c8930 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -89,11 +89,9 @@ if ($ENV{REQUEST_URI}) { # http-backend.c for how I got that. Also note that "info" is overloaded; # git uses "info/refs...", while gitolite uses "info" or "info?...". So # there's a "/" after info in the list below - if ($ENV{PATH_INFO} =~ m(/(HEAD$|info/refs$|objects/|git-(?:upload|receive)-pack$))) { - my ($repo) = ($ENV{PATH_INFO} =~ m(^/(.*)\.git(?:/|$))); - my ($verb) = ($ENV{REQUEST_URI} =~ m((git-(?:receive|upload)-pack))); - print STDERR "(gitolite) no verb found in $ENV{REQUEST_URI}\n" unless $verb; - $verb ||= 'git-upload-pack'; + if ($ENV{PATH_INFO} =~ m(^/(.*)/(HEAD$|info/refs$|objects/|git-(?:upload|receive)-pack$))) { + my $repo = $1; + my $verb = ($ENV{REQUEST_URI} =~ /git-receive-pack/) ? 'git-receive-pack' : 'git-upload-pack'; $ENV{SSH_ORIGINAL_COMMAND} = "$verb '$repo'"; } else { # this is one of our custom commands; could be anything really, From b6c93c7ce871664c07c244a2587233e180fdf3e0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 5 Sep 2010 19:58:31 +0530 Subject: [PATCH 474/850] (http) I'm sorry DAV, I can't let you do that... --- src/gl-auth-command | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gl-auth-command b/src/gl-auth-command index 10c8930..15fdfaf 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -85,6 +85,8 @@ if (@ARGV and $ARGV[0] eq '-s') { my $user; if ($ENV{REQUEST_URI}) { + die "fallback to DAV not supported\n" if $ENV{REQUEST_METHOD} eq 'PROPFIND'; + # these patterns indicate normal git usage; see "services[]" in # http-backend.c for how I got that. Also note that "info" is overloaded; # git uses "info/refs...", while gitolite uses "info" or "info?...". So From 595410aa55315de10a11428887c74399970350c5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 6 Sep 2010 10:34:21 +0530 Subject: [PATCH 475/850] (http) first level error reporting now works I didn't know that remote-curl.c requires 200 OK even if you want to report an error. With Ilari's patch at [1] you'll get a more readable message but it is still good enough now. [1]: http://permalink.gmane.org/gmane.comp.version-control.git/155464 --- doc/http-backend.mkd | 12 ------------ src/gitolite.pm | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/doc/http-backend.mkd b/doc/http-backend.mkd index fbc7300..cf1af10 100644 --- a/doc/http-backend.mkd +++ b/doc/http-backend.mkd @@ -22,18 +22,6 @@ In this document: `~/.netrc` file and making it `chmod 600`. Anyway, http based access is inherently less secure than pubkeys so not much point worrying about it. - * messages from first level errors (where gitolite's 'gl-auth-command' bombs - out without letting git even get control, due to inadequate access) don't - come to the user. The git client is swallowing up a perfectly good error - message -- you can see it if you manually retry like this: - - $ curl http://bob:bob@127.0.0.1/git/foo/alice/a1.git/info/refs?service=git-receive-pack - W access for foo/alice/a1 DENIED to bob - - What's worse, when this failure happens, the git client retries a few - other things, eventually ending up doing a `PROPFIND` webdav attempt so - you get a DAV error of some kind. - * I have not tested any of the ancillary standalone programs (like gl-dont-panic) in this mode. They're most likely going to crash and burn because `$HOME` is not defined or in the wrong place; manually set diff --git a/src/gitolite.pm b/src/gitolite.pm index 863683b..1a6ab6a 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -242,7 +242,7 @@ sub where_is_rc $SIG{__DIE__} = sub { my $msg = shift; chomp($msg); - &print_http_headers(500, "error - gitolite"); + &print_http_headers(); # remote-curl.c requires 200 OK even if you want to report an error print "$msg\r\n"; print STDERR "$msg\n"; exit 0; # if it's ok for die_webcgi in git.git/http-backend.c, it's ok for me ;-) From f10fa00227b1410045426d667fcf711e31e324f9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 8 Sep 2010 15:43:56 +0530 Subject: [PATCH 476/850] gl-setup now allows editing rc file on first run --- src/gl-setup | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gl-setup b/src/gl-setup index f0a3530..e72ebc6 100755 --- a/src/gl-setup +++ b/src/gl-setup @@ -60,6 +60,10 @@ else [ -z "$GITOLITE_HTTP_HOME" ] || [ -n "$admin_name" ] || die "looks like first run -- I need an admin name" cp $GL_PACKAGE_CONF/example.gitolite.rc $HOME/.gitolite.rc + printf "The default settings in the "rc" file ($HOME/.gitolite.rc) are fine for most\n" + printf "people but if you wish to make any changes, you can do so now.\n\nhit enter..." + read i + ${EDITOR:-vi} $HOME/.gitolite.rc fi # setup ssh stuff. We break our normal rule that we will not fiddle with From 4dd17b96cb7f161112dde97daf8be5e99304f534 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 8 Sep 2010 19:34:38 +0530 Subject: [PATCH 477/850] (http) https tested --- doc/http-backend.mkd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/http-backend.mkd b/doc/http-backend.mkd index cf1af10..0c04f84 100644 --- a/doc/http-backend.mkd +++ b/doc/http-backend.mkd @@ -31,7 +31,8 @@ In this document: * tested on stock Fedora 13; if you test on other environments please let me know how it worked out and if we need to adjust this document - * tested only http, not https; if you do, and it works, please tell me + * tested https with dummy certs and `GIT_SSL_NO_VERIFY`; no reason why it + shouldn't work on a proper setup with everything in place * have not tried making repos available to both ssh *and* http mode clients; (I'd guess it ought to work fine if the "apache" user was made login-able From 2349afdc780e271bf25f901013dbcae4d126f5b1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 8 Sep 2010 19:57:11 +0530 Subject: [PATCH 478/850] (http) fixed up the first level error reporting even better see http://permalink.gmane.org/gmane.comp.version-control.git/155787 --- src/gitolite.pm | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 1a6ab6a..cef3bf0 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -241,10 +241,22 @@ sub where_is_rc $ENV{HOME} = $ENV{GITOLITE_HTTP_HOME}; $SIG{__DIE__} = sub { - my $msg = shift; chomp($msg); - &print_http_headers(); # remote-curl.c requires 200 OK even if you want to report an error - print "$msg\r\n"; - print STDERR "$msg\n"; + my $service = ($ENV{SSH_ORIGINAL_COMMAND} =~ /git-receive-pack/ ? 'git-receive-pack' : 'git-upload-pack'); + my $message = shift; chomp($message); + print STDERR "$message\n"; + + # format the service response, then the message. With initial + # help from Ilari and then a more detailed email from Shawn... + $service = "# service=$service\n"; $message = "ERR $message\n"; + $service = sprintf("%04X", length($service)+4) . "$service"; # no CRLF on this one + $message = sprintf("%04X", length($message)+4) . "$message"; + + &print_http_headers(); + print $service; + print "0000"; # flush-pkt, apparently + print $message; + print STDERR $service; + print STDERR $message; exit 0; # if it's ok for die_webcgi in git.git/http-backend.c, it's ok for me ;-) } } From 37a6a6a7d36cf55c1d7caa50d9a4959e643196c4 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 9 Sep 2010 06:52:32 +0530 Subject: [PATCH 479/850] towel needed more clarity for people who don't get the continual reference to towels when talking about the "gl-dont-panic" program, all I can say is that your education is incomplete ;-) --- src/gl-dont-panic | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/gl-dont-panic b/src/gl-dont-panic index 9476c96..7bd4ea9 100755 --- a/src/gl-dont-panic +++ b/src/gl-dont-panic @@ -19,13 +19,21 @@ usage() { - (FIX #1: REWINDING BAD ADMIN COMMITS) if your last commit(s) to the gitolite-admin repo pushed a very bad config and you want to rewind it to a known good state, run this: - ./gl-dont-panic rewind + + $0 rewind + (this doesn't actually rewind; it creates a new commit that has the same state as the last good commit, which has the same effect) - - (FIX #2: PUSHING A NEW ADMIN KEY) if you lost your admin key, run this - with the new pubkey: - ./gl-dont-panic sitaram.pub + - (FIX #2: PUSHING A NEW ADMIN KEY) if you lost your admin key, or you + had used the wrong key initially, then you get yourself a new keypair + and run this with the new pubkey: + + $0 sitaram.pub # example using my name + + Please note that this simply *replaces* the key for user "sitaram". + It does NOT add a new admin called "sitaram". In fact it does not + touch the config file (access rules) at all. Step 3: completing the fix (on your workstation) From 3f57f18ee2aeddf6868970d69a74dd8db197ee46 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 23 Sep 2010 14:04:04 +0530 Subject: [PATCH 480/850] wildrepos doc badly needed update due to new features: - 79f0a5f ("(big one!) more than one wildcard may match a repo...") makes some of the dire warnings about this irrelevant - d1d2c3e and ad64f99 ("git config settings in wild repos: part 1" and "...part 2") makes this caveat also useless While we were about it, we added a quick intro and tried to make some other details a little clearer. --- doc/wildcard-repositories.mkd | 66 +++++++++++++++-------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/doc/wildcard-repositories.mkd b/doc/wildcard-repositories.mkd index 974455e..2c7241f 100644 --- a/doc/wildcard-repositories.mkd +++ b/doc/wildcard-repositories.mkd @@ -8,19 +8,13 @@ created it, allowing him/her to hand out R and RW permissions to other users to collaborate, all these are possible. And any of these could have a bug in it. I haven't found any yet, but that doesn't mean there aren't any. -Also, there are some limitations. For example, you cannot specify gitconfig -values for a wildcard repo; it only works for actual repos. - -There may be other such missing features. Sometimes it's just not possible to -make it work. Or it may be cumbersome enough that unless there are *no* -workarounds I may not have the time to code it right away. - ---- In this document: + * quick introduction * rc file setting required - * wildcard repos + * examples of wildcard repos * wildcard repos with creator name in them * wildcard repos without creator name in them * side-note: line-anchored regexes @@ -28,7 +22,6 @@ In this document: * handing out rights to wildcard-matched repos * setting a gitweb description for a wildcard-matched repo * reporting - * other issues and discussion * how it actually works ---- @@ -37,6 +30,16 @@ This document is mostly "by example". ---- + + +### quick introduction + +The wildrepos feature allows you to specify access control rules using regular +expression patterns, so you can have many actual repos being served by a +single set of rules in the config file. The regex pattern can also include +the word `CREATOR` in it, allowing you to parametrise the name of the user +creating the repo. The examples below will make this clearer. + ### rc file setting required @@ -45,13 +48,24 @@ This feature requires that you set `$GL_WILDREPOS` to "1" in `~/.gitolite.rc` on the server. Please search for that variable and see comments around it in `conf/example.gitolite.rc` for more information on this. - + -### wildcard repos +### examples of wildcard repos + +As the introduction said, you can include the word `CREATOR` in the regex +pattern, though it is not mandatory. We'll look at examples of both types of +usage. Which of these alternatives you choose depends on your needs, and the social -aspects of your environment. The first one is a little more rigid, making it -harder to make mistakes, and the second is more flexible and trusting. +aspects of your environment. Including the creator name in the pattern keeps +users rigidly separated from each others repos, and is good for a largely +autonomous collection of users with a high probability of repo name clashes. + +Omitting the creator name from the pattern puts the repos in a common +namespace, and is suitable for environments where it is not very important to +keep track of who actually created the repo (except for granting access), but +needs more communication / co-operation among the users to avoid repo name +clashes. @@ -196,28 +210,6 @@ commands, thanks to Teemu. In order to see what repositories were created from a wildcard, use the "expand" command, described briefly in [doc/report-output.mkd][repout]. - - -### other issues and discussion - - * *what if the repo name being pushed matches more than one pattern*? - - I think it would be very hard to reason about access if we were to do - something like combine all the access rights in all the matching patterns. - No matter how you do it, and how carefully you document it, there'll be - someone who is surprised by the result. - - And in security, that's a ***Bad Thing***. - - So we don't combine permissions. At runtime, we die if we find more than - one match. Let 'em go holler at the admin for creating multiple matching - repo patterns :-) - - This can make some repos inaccessible if the patterns changed *after* they - were created. The administrator should be careful not to do this. Most - of the time, it won't be difficult; the fixed prefix will usually be - different anyway so there won't be overlaps. - ### how it actually works @@ -249,8 +241,8 @@ who actually *created* the repo (this name is recorded in a special file in the repo directory when the repo is first created, so it is available). Now find a repo pattern that matches the actual reponame being pushed -- this -tells you which set of rules to apply. Only one pattern is allowed to match; -matching more than one is an error. +tells you which set of rules to apply. There can be multiple matches; if so, +they will all be applied in the sequence they appear in the config file. If the invoking user has been given "RW" permissions using `setperms`, all occurrences of the word WRITERS are replaced by the invoking username. From c18514e2139af0df9cd50e823a08af63b76a6a65 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 20 Sep 2010 23:18:28 +0530 Subject: [PATCH 481/850] (minor) doc updates inspired by various denizens of #git and the internet at large --- doc/2-admin.mkd | 23 +++++++++++++++-------- doc/install-transcript.mkd | 5 +++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index 5f979c2..d250077 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -140,15 +140,22 @@ for the special usernames or remove the description line. #### custom hooks -You can supply your own, custom, hook scripts if you wish. Just put a -**tested** hook script in `hooks/common` of your gitolite clone (as -distributed, there are only two files there). For each file in that -directory, a symlink pointing to it will be placed in the `hooks/` -subdirectory of every *new* repo created. +You can supply your own, custom, hook scripts if you wish. Install gitolite +as usual, then: -If you added any new hooks and wish to propagate them to *existing* repos as -well, just run gl-easy-install (or gl-setup, if you installed directly on the -server) once. + * if you installed using "from-client" method (gl-easy-install): + * go to the gitolite *source* clone from which you did the original + install + * add your new hook into "hooks/common" + * run src/gl-easy-install with the same arguments as you ran the first + time + * if you installed using one of the other methods + * go to ~/.gitolite/hooks/common on the server and put your new hook + there + * now run "gl-setup" again + +You can use this procedure to install new hooks as well as to update hooks +that you had previously installed. **VERY IMPORTANT SECURITY NOTE: the `update` hook in `hooks/common` is what implements all the branch-level permissions in gitolite. If you fiddle with diff --git a/doc/install-transcript.mkd b/doc/install-transcript.mkd index 67093ed..71c97d1 100644 --- a/doc/install-transcript.mkd +++ b/doc/install-transcript.mkd @@ -85,6 +85,11 @@ because I'm not showing the actual "vi" session): Stopping sshd: [ OK ] Starting sshd: [ OK ] +**NOTE**: if the `AllowUsers` setting is completely missing from the sshd +config file, all users are allowed (see `man sshd_config`). You may prefer to +leave it that way -- your choice. I prefer to make the usernames explicit +because I'm paranoid ;-) + ---- From 67a72a3f5b3b28bc685d34521b3037832a65bac1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 24 Sep 2010 16:19:33 +0530 Subject: [PATCH 482/850] added support for a post-repo-create hook (gl-post-init) ...some people want to run a special function after a repo is created --- contrib/adc/fork | 3 + doc/2-admin.mkd | 150 ++++++++++++++++++++++++++--------------------- src/gitolite.pm | 6 ++ 3 files changed, 91 insertions(+), 68 deletions(-) diff --git a/contrib/adc/fork b/contrib/adc/fork index 70deb4c..224be36 100755 --- a/contrib/adc/fork +++ b/contrib/adc/fork @@ -27,3 +27,6 @@ if [ -n "$GL_WILDREPOS_DEFPERMS" ]; then echo "$GL_WILDREPOS_DEFPERMS" > gl-perms fi +# run gitolite's post-init hook if you can (hook code expects GL_REPO to be set) +export GL_REPO; GL_REPO="$to" +[ -x hooks/gl-post-init ] && hooks/gl-post-init diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index d250077..c7e9efb 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -4,12 +4,14 @@ In this document: * please read this first * adding users and repos + * using hooks + * custom hooks + * "gl-post-init" hook + * hook chaining + * environment variables available to hooks * other features * moving pre-existing repos into gitolite * specifying gitweb and daemon access - * custom hooks - * hook chaining - * environment variables available to hooks * custom git config ---- @@ -65,6 +67,83 @@ Once you've cloned it, you're ready to add users and repos. automatically be created (empty, but clonable) and users' access will be updated as needed. + + +### using hooks + + + +#### custom hooks + +You can supply your own, custom, hook scripts if you wish. Install gitolite +as usual, then: + + * if you installed using "from-client" method (gl-easy-install): + * go to the gitolite *source* clone from which you did the original + install + * add your new hook into "hooks/common" + * run src/gl-easy-install with the same arguments as you ran the first + time + * if you installed using one of the other methods + * go to ~/.gitolite/hooks/common on the server and put your new hook + there + * now run "gl-setup" again + +You can use this procedure to install new hooks as well as to update hooks +that you had previously installed. + +**VERY IMPORTANT SECURITY NOTE: the `update` hook in `hooks/common` is what +implements all the branch-level permissions in gitolite. If you fiddle with +the hooks directory, please make sure you do not mess with this file +accidentally, or all your fancy per-branch permissions will stop working.** + + + +#### "gl-post-init" hook + +Sometimes it is necessary to do something whenever a new repo is created. If +you need this functionality, just supply a hook called "gl-post-init" with +whatever code you want in it. + + + +#### hook chaining + +Gitolite basically takes over the update hook for all repos, but some setups +really need the update hook functionality for their own purposes too. In +order to allow this, Gitolite now exec's a hook called `update.secondary` when +it's own "update" hook is done and everything is ready to go. + +You can create this `update.secondary` hook manually on selected repos on the +server, or use the mechanism in the previous section to make gitolite put it +on *all* your repos. + +Similarly, gitolite also takes over the post-update hook for the special +"gitolite-admin" repo. This hook will also chain to a `post-update.secondary` +if such a hook exists. People wishing to do exotic things on the server side +when the admin repo is pushed should see doc/shell-games.notes for how to +exploit this :-) + +Finally, these names (`update.secondary` and `post-update.secondary`) are +merely the defaults. You can change them to anything you want; look in +conf/example.gitolite.rc for details. + + + +#### environment variables available to hooks + +The following environment variables are set, and may be useful for any custom +processing you wish to do in your hook code: + + * `GL_USER` -- the user doing the push + * `GL_REPO` -- the reponame + * `GL_REPO_BASE_ABS` -- the absolute base path where all the repos are kept + +The following variables are also set, but are generally less useful: + + * `GL_BINDIR` -- where all the binaries live + * `GL_ADMINDIR` -- common directory for many gitolite things + ### other features @@ -136,71 +215,6 @@ The "compile" script will keep these files consistent with the config settings -- this includes removing such settings/files if you remove "read" permissions for the special usernames or remove the description line. - - -#### custom hooks - -You can supply your own, custom, hook scripts if you wish. Install gitolite -as usual, then: - - * if you installed using "from-client" method (gl-easy-install): - * go to the gitolite *source* clone from which you did the original - install - * add your new hook into "hooks/common" - * run src/gl-easy-install with the same arguments as you ran the first - time - * if you installed using one of the other methods - * go to ~/.gitolite/hooks/common on the server and put your new hook - there - * now run "gl-setup" again - -You can use this procedure to install new hooks as well as to update hooks -that you had previously installed. - -**VERY IMPORTANT SECURITY NOTE: the `update` hook in `hooks/common` is what -implements all the branch-level permissions in gitolite. If you fiddle with -the hooks directory, please make sure you do not mess with this file -accidentally, or all your fancy per-branch permissions will stop working.** - - - -#### hook chaining - -Gitolite basically takes over the update hook for all repos, but some setups -really need the update hook functionality for their own purposes too. In -order to allow this, Gitolite now exec's a hook called `update.secondary` when -it's own "update" hook is done and everything is ready to go. - -You can create this `update.secondary` hook manually on selected repos on the -server, or use the mechanism in the previous section to make gitolite put it -on *all* your repos. - -Similarly, gitolite also takes over the post-update hook for the special -"gitolite-admin" repo. This hook will also chain to a `post-update.secondary` -if such a hook exists. People wishing to do exotic things on the server side -when the admin repo is pushed should see doc/shell-games.notes for how to -exploit this :-) - -Finally, these names (`update.secondary` and `post-update.secondary`) are -merely the defaults. You can change them to anything you want; look in -conf/example.gitolite.rc for details. - - - -#### environment variables available to hooks - -The following environment variables are set, and may be useful for any custom -processing you wish to do in your hook code: - - * `GL_USER` -- the user doing the push - * `GL_REPO` -- the reponame - * `GL_REPO_BASE_ABS` -- the absolute base path where all the repos are kept - -The following variables are also set, but are generally less useful: - - * `GL_BINDIR` -- where all the binaries live - * `GL_ADMINDIR` -- common directory for many gitolite things - #### custom git config diff --git a/src/gitolite.pm b/src/gitolite.pm index cef3bf0..9733cf8 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -294,6 +294,12 @@ sub new_repo # override with the package hooks ln_sf("$GL_PACKAGE_HOOKS/common", "*", "hooks") if $GL_PACKAGE_HOOKS; chmod 0755, "hooks/update"; + + # run gitolite's post-init hook if you can. GL_REPO will be correct on a + # wildcard create but on a normal (config file) create it will actually be + # set to "gitolite-admin", so we need to make sure that for the duration + # of the hook it is set correctly. + system("env GL_REPO='$repo' hooks/gl-post-init") if -x "hooks/gl-post-init"; } # ---------------------------------------------------------------------------- From da9a1b5cbf002be46b3c812979cc9b2733f36aef Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 28 Sep 2010 11:46:39 +0530 Subject: [PATCH 483/850] gitosis migr doc update to handle user@foo type keys thanks to frogonwheels for catching this... --- doc/migrate.mkd | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/migrate.mkd b/doc/migrate.mkd index dd27b8b..9d9489a 100644 --- a/doc/migrate.mkd +++ b/doc/migrate.mkd @@ -55,7 +55,24 @@ Now, log off the server and get back to the client: cp $GSAC/keydir/* $GLAC/keydir -4. **Important: expand any multi-key files you may have**. [Here][mk]'s an +4. **IMPORTANT**: if you have any users with names like `user@foo`, where the + part after the `@` does *not* have a `.` in it (i.e., does not look like + an email address), you need to change them, because gitolite uses that + syntax for enabling multi keys. + + You have two choices in how to fix this. You can change the gitolite + config so that all mention of `user@foo` is changed to just `user`. + + Or you can change each occurrence of `user@foo` to, say, `user_foo` *and* + change the pubkey filename in keydir/ also the same way (`user_foo.pub`). + + Just to repeat, you do NOT need to do this if the username was like + `user@foo.bar`, i.e., the part after the `@` had a `.` in it, because then + it looks like an email address. + + [This][mk] will tell you more about these nuances. + +5. **IMPORTANT: expand any multi-key files you may have**. [Here][mk]'s an explanation of what multi-keys are, how gitosis does them and how gitolite does it differently. @@ -82,7 +99,7 @@ Now, log off the server and get back to the client: "sitaram@laptop.pub" and "sitaram@desktop.pub" or whatever. *Please check the files to make sure this worked properly* -5. Check all your changes to your gitolite-admin clone, commit, and push +6. Check all your changes to your gitolite-admin clone, commit, and push [mk]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#multikeys [inst]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd From 4957900360e714c45bd33bd0fbbda819b9bea74b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 28 Sep 2010 20:03:25 +0530 Subject: [PATCH 484/850] (doc) who-uses-it --- README.mkd | 4 ++++ doc/who-uses-it.mkd | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 doc/who-uses-it.mkd diff --git a/README.mkd b/README.mkd index 6e91578..b93c28d 100644 --- a/README.mkd +++ b/README.mkd @@ -16,6 +16,9 @@ new, the suggested reading order is this: If you run into trouble start [here](#support). If you're migrating from gitosis, read [this][migr]. +And [here][who]'s some information on some of the projects and people using +gitolite (and who, in turn, have helped shape its features). + Once you've installed it and started using it, you'll want to explore some of the more powerful features. All the documentation is available in the source repo as well as [online][docs]. All the longer documents have tables of @@ -190,4 +193,5 @@ Gitolite is released under GPL v2. See COPYING for details. [docs]: http://github.com/sitaramc/gitolite/blob/pu/doc [doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite-and-ssh.mkd [doc6sts]: http://github.com/sitaramc/gitolite/blob/pu/doc/ssh-troubleshooting.mkd +[who]: http://github.com/sitaramc/gitolite/blob/pu/doc/who-uses-it.mkd diff --git a/doc/who-uses-it.mkd b/doc/who-uses-it.mkd new file mode 100644 index 0000000..c15eede --- /dev/null +++ b/doc/who-uses-it.mkd @@ -0,0 +1,41 @@ +# who uses gitolite + +> > If you're using gitolite and find it very useful in some way, I would +> > love to describe your use of it or add a link to your own description +> > of it here. Of course, you can anonymise it as much as you need to. + +The **Fedora Project** controls access to over 10,000 package management +repositories accessed by over 1,000 package maintainers [using +gitolite][fedora]. This is probably the largest *confirmed* gitolite +installation anywhere. The whole [big-config][bc] thing was initially done +for them (their config file was so big that without the big-config changes +gitolite would just run out of memory and die!). + +[fedora]: http://lists.fedoraproject.org/pipermail/devel-announce/2010-July/000647.html +[bc]: http://github.com/sitaramc/gitolite/blob/pu/doc/big-config.mkd + +The **KDE project** is currently [testing][kdet] gitolite to see if it will +suit their needs (in combination with redmine for issue tracking and +reviewboard for code review), after an initial [review of alternatives][kdera] +by a core group. Apart from the usual access control, the KDE folks will be +using many of the "ad hoc repo creation" features enabled by wildrepos and the +accompanying commands. Some of the changes to the "admin defined commands" +were inspired by KDE's needs. + +[kdet]: http://www.omat.nl/2010/07/07/move-to-git-the-progress-so-far/ +[kdera]: http://permalink.gmane.org/gmane.comp.kde.scm-interest/1437 + +**Prof. Hiren Patel** of the University of Waterloo is responsible for the +existence of the fairly popular "[wildrepos][wild]" feature. The +documentation was pretty much written with his use case in mind, but of course +it turns out to be useful for a lot of people. + +In fact, he surprised the heck out of me recently by saying that if it hadn't +been for this feature, he might not have used git itself -- which is a pretty +serious compliment if you think about the magnitude of the git project and my +little one-man show! + +He explains his use of it [here][hiren]. + +[wild]: http://github.com/sitaramc/gitolite/blob/pu/doc/wildcard-repositories.mkd +[hiren]: http://ece.uwaterloo.ca/~hdpatel/uwhtml/?p=470 From 3914dc0161a21653e927a2d176246bf9dead1a85 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 2 Oct 2010 05:22:19 +0530 Subject: [PATCH 485/850] tighten permissions on install - hardcode 0700 mode for GL_ADMINDIR tree (thanks to ma at ibitsense.com) for catching this - honor REPO_UMASK for GL_REPO_BASE_ABS creation - plus a minor doc update --- doc/install-transcript.mkd | 4 +++- src/gl-install | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/doc/install-transcript.mkd b/doc/install-transcript.mkd index 71c97d1..1a3bcbc 100644 --- a/doc/install-transcript.mkd +++ b/doc/install-transcript.mkd @@ -169,7 +169,9 @@ runs on the client but installs gitolite on the server! **This is the only gitolite specific command in a typical install sequence**. Run it without any arguments to see a usage message. Run it without the `-q` to get a more verbose, pause-at-every-step, -install mode that allows you to change the defaults etc. +install mode that allows you to change the defaults (for example, if you want +a different UMASK setting, or you want the repos to be in a different place, +etc.) sita@sita-lt:src $ ./gl-easy-install -q git server sitaram you are upgrading (or installing first-time) to v0.95-38-gb0ce84d diff --git a/src/gl-install b/src/gl-install index db1affc..e941092 100755 --- a/src/gl-install +++ b/src/gl-install @@ -5,7 +5,7 @@ use strict; use warnings; -our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $GL_PERFLOGT); +our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $GL_PERFLOGT, $REPO_UMASK); # setup quiet mode if asked; please do not use this when running manually open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); @@ -15,11 +15,13 @@ open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); sub wrap_mkdir { my $dir = shift; + my $perm = shift; # optional if ( -d $dir ) { print "$dir already exists\n"; return; } mkdir($dir) or die "mkdir $dir failed: $!\n"; + chmod $perm, $dir if $perm; print "created $dir\n"; } @@ -49,14 +51,17 @@ die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; # add a custom path for git binaries, if specified $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; +# set the umask before creating any files/directories +umask($REPO_UMASK); + # mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist $ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" ); wrap_mkdir($ENV{GL_REPO_BASE_ABS}); -wrap_mkdir($GL_ADMINDIR); +wrap_mkdir($GL_ADMINDIR, 0700); # mkdir $GL_ADMINDIR's subdirs for my $dir qw(conf doc keydir logs src hooks hooks/common hooks/gitolite-admin) { # some of them will stay empty; too lazy to fix right now ;-) - wrap_mkdir("$GL_ADMINDIR/$dir"); + wrap_mkdir("$GL_ADMINDIR/$dir", 0700); } # "src" and "doc" will be overwritten on each install, but not conf From 91f1b74503ce66b79c2db9d5a7abb1338275cee1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 3 Oct 2010 08:48:59 +0530 Subject: [PATCH 486/850] document $GL_ALL_INCLUDES_SPECIAL (and clarify/refactor the docs a bit) --- doc/2-admin.mkd | 24 +++++++++++++++++++----- doc/3-faq-tips-etc.mkd | 19 ++++--------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/doc/2-admin.mkd b/doc/2-admin.mkd index c7e9efb..78e2ab1 100644 --- a/doc/2-admin.mkd +++ b/doc/2-admin.mkd @@ -180,6 +180,8 @@ to take a bunch of existing repos and add them to gitolite: `conf/gitolite.conf` in your gitolite-admin repo clone. Then add, commit, push. + + #### specifying gitweb and daemon access @@ -188,10 +190,15 @@ This is a feature that I personally do not use (corporate environments don't like unauthenticated access of any kind to any repo!), but someone wanted it, so here goes. -To make a repo or repo group accessible via "git daemon", just give read -permission to the special user "daemon". +Gitolite defines two "special" usernames: `daemon` and `gitweb`. + +To make a repo or repo group accessible via "git daemon", just give read +permission to the special user "daemon". Similarly, give read permission to +`gitweb` to allow the gitweb CGI to show the repo. + +This gives you a quick way to offer multiple repos up for gitweb/daemon +access. -There's a special user called "gitweb" also, which works the same way. However, setting a description for the project also enables gitweb permissions so you may as well use that method and kill two birds with one stone, like so: @@ -201,8 +208,8 @@ You can also specify an owner for gitweb to show, if you like: gitolite "Sitaram Chamarty" = "fast, secure, access control for git in a corporate environment" -Note that gitolite does **not** install or configure gitweb/daemon -- that is -a one-time setup you must do separately. All this does is: +Note that gitolite does **not** install or configure gitweb/git-daemon -- that +is a one-time setup you must do separately. All gitolite does is: * for daemon, create the file `git-daemon-export-ok` in the repository * for gitweb, add the repo (plus owner name, if given) to the list of @@ -215,6 +222,13 @@ The "compile" script will keep these files consistent with the config settings -- this includes removing such settings/files if you remove "read" permissions for the special usernames or remove the description line. +Please **note** that giving permissions to these special users via `@all` +(that is, using either `repo @all` or `R = @all`), will not work unless you +set the rc-file variable `$GL_ALL_INCLUDES_SPECIAL` to `1`. Also, **NOTE** +that giving them read access to `repo @all` means the `gitolite-admin` repo is +also accessible. **It is upto you to decide if that is OK in your +environment**. + #### custom git config diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 466471f..46fe7ac 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -569,22 +569,11 @@ complete, you can do it all from within the gitolite config file! ##### easier to specify gitweb "description" and gitweb/daemon access -To enable access to a repo via gitweb *and* create a "description" for it to -show up on the webpage, just add a line like this, anywhere in the config -file: +Please see [gwd] for details on how to do this if you've never done this +before. This section is only about how gitolite makes it easy to specify +different combinations of access for different sets of repos. - reponame = "one line of description" - -You can also specify an "owner": - - reponame "owner name" = "one line of description" - -To enable access to one or more repos via git daemon, just give "read" -permissions to the special username `daemon`. - -There is also a special user called `gitweb` to specify gitweb access; useful -if you don't care about specifying individual descriptions for each repo and -just want to quickly enable gitweb access to one or more repos. +[gwd]: http://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd#gwd Remember gitolite lets you specify the access control specs in bits and pieces, so you can keep all the daemon/gitweb access in one place, even if From 112f6fab57e0f7a38ba38d4b133c2c936fce391d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 3 Oct 2010 09:08:34 +0530 Subject: [PATCH 487/850] mirror-shell needs to honor REPO_UMASK --- src/gl-mirror-shell | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gl-mirror-shell b/src/gl-mirror-shell index 6689aef..150f132 100755 --- a/src/gl-mirror-shell +++ b/src/gl-mirror-shell @@ -4,6 +4,9 @@ export GL_BYPASS_UPDATE_HOOK GL_BYPASS_UPDATE_HOOK=1 export REPO_BASE=`cd $HOME;perl -e 'do ".gitolite.rc"; print $REPO_BASE' ` +export REPO_UMASK=`cd $HOME;perl -e 'do ".gitolite.rc"; print $REPO_UMASK' ` +umask $REPO_UMASK + if echo $SSH_ORIGINAL_COMMAND | egrep git-upload\|git-receive >/dev/null then From b851011ce5bf2c06ad2559c292ffead2059a788b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 4 Oct 2010 14:28:58 +0530 Subject: [PATCH 488/850] new RC file variable $GL_SITE_INFO to show site-specific info --- conf/example.gitolite.rc | 13 +++++++++++++ src/gitolite.pm | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index db88dbd..e189595 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -260,6 +260,19 @@ $GL_WILDREPOS = 0; # $GL_ADC_PATH = ""; +# -------------------------------------- +# SITE-SPECIFIC INFORMATION + +# Some installations would like to give their users customised information +# (like a link to their own websites, for example) so that each end user does +# not have to grok all the gitolite documentation. + +# If this variable is defined, the "info" command will print it at the end of +# the listing. + +# $GL_SITE_INFO = ""; +# $GL_SITE_INFO = "XYZ.COM DEVELOPERS: PLEASE SEE http://xyz.com/gitolite/help first"; + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/src/gitolite.pm b/src/gitolite.pm index 9733cf8..23f7fe5 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -38,7 +38,7 @@ our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple patter our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); # these come from the RC file -our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST, $GL_ALL_INCLUDES_SPECIAL); +our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST, $GL_ALL_INCLUDES_SPECIAL, $GL_SITE_INFO); our %repos; our %groups; our %repo_config; @@ -622,6 +622,7 @@ sub report_basic print "$perm\t$r\r\n" if $perm =~ /\S/; } print "only 20 out of $count candidate repos examined\r\nplease use a partial reponame or regex pattern to limit output\r\n" if $GL_BIG_CONFIG and $count > 20; + print "$GL_SITE_INFO\n" if $GL_SITE_INFO; } # ---------------------------------------------------------------------------- @@ -656,6 +657,7 @@ sub expand_wild print "$perm\t$creator\t$actual_repo\n"; } print "only 20 out of $count candidate repos examined\nplease use a partial reponame or regex pattern to limit output\n" if $GL_BIG_CONFIG and $count > 20; + print "$GL_SITE_INFO\n" if $GL_SITE_INFO; } # there will be multiple calls to repo_rights; better to use a closure. We From dc62d69848a971625b2ab545aceda388cfaa11ee Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 4 Oct 2010 19:28:33 +0530 Subject: [PATCH 489/850] progit doc... thanks to tsgarp for making me think about adding this caution --- doc/progit-article.mkd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/progit-article.mkd b/doc/progit-article.mkd index ce967f8..da016d3 100644 --- a/doc/progit-article.mkd +++ b/doc/progit-article.mkd @@ -1,5 +1,9 @@ ## Gitolite ## +Note: the latest copy of this section of the ProGit book is always available within the [gitolite documentation][gldpg]. The author would also like to humbly state that, while this section is accurate, and *can* (and often *has*) been used to install gitolite without reading any other documentation, it is of necessity not complete, and cannot completely replace the enormous amount of documentation that gitolite comes with. + +[gldpg]: http://github.com/sitaramc/gitolite/blob/pu/doc/progit-article.mkd + Git has started to become very popular in corporate environments, which tend to have some additional requirements in terms of access control. Gitolite was originally created to help with those requirements, but it turns out that it's equally useful in the open source world: the Fedora Project controls access to their package management repositories (over 10,000 of them!) using gitolite, and this is probably the largest gitolite installation anywhere too. Gitolite allows you to specify permissions not just by repository, but also by branch or tag names within each repository. That is, you can specify that certain people (or groups of people) can only push certain "refs" (branches or tags) but not others. From 51ef9517685c320d2cb918b598fc53677ee97491 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 5 Oct 2010 20:33:32 +0530 Subject: [PATCH 490/850] gl-reflog -- first version; proof of concept only, NOT for production use (yet) --- contrib/adc/gl-reflog | 90 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100755 contrib/adc/gl-reflog diff --git a/contrib/adc/gl-reflog b/contrib/adc/gl-reflog new file mode 100755 index 0000000..cffee91 --- /dev/null +++ b/contrib/adc/gl-reflog @@ -0,0 +1,90 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +# - show fake "reflog" from gitolite server +# - recover deleted branches +# - recover from bad force pushes + +# -------------------- + +# - PROOF OF CONCEPT ONLY +# - NO ARGUMENT OR ERROR CHECKING DONE; DO NOT USE IN PRODUCTION UNTIL THAT IS FIXED + +# -------------------- + +# WARNING +# - heavily dependent on the gitolite log file format (duh!) + +# USAGE +# ssh git@server gl-reflog show r1 refs/heads/b1 +# # shows last 10 updates to branch b1 in repo r1 +# ssh git@server gl-reflog show r1 refs/heads/b1 20 +# # shows last 20 entries... +# ssh git@server gl-reflog recover r1 refs/heads/b1 +# # recovers the last update to b1 in r1 if it was a "+" + +# NOTES +# - the verb "recover" is used because this is expected to be used most often +# to recover deleted branches. Plus there's enough confusion in git land +# caused by "reset" and "revert" I thought I should add my bit to it ;-) +# - git's internal reflog is NOT recovered, even if you recover the branch. +# I'm good but not *that* good ;-) +# - since this program produces a log entry that satisfies it's own criteria, +# it acts as a "toggle" for its own action for rewinds (but not for deletes) + +my($cmd, $repo, $ref, $limit) = @ARGV; +$limit ||= 10; + +my @logfiles = sort glob("$ENV{GL_ADMINDIR}/logs/*"); + +# TODO figure out how to avoid reading *all* the log files when you really +# only need the last few + +our @loglines; +{ + my @f; + local(@ARGV) = @logfiles; + while (<>) { + chomp; + @f = split /\t/; + # field 2 is the userid, 5 is W or +, 6/7 are old/new SHAs + # 8 is reponame, 9 is refname (but all those are 1-based) + next unless $f[1] eq $ENV{GL_USER}; + next unless $f[3] =~ /^(git-receive-pack|gl-reflog recover) /; + next unless $f[8]; + next unless $f[7] eq $repo; + next unless $f[8] eq $ref; + push @loglines, $_; + } +} + +if ( $cmd eq 'show' ) { + my $start = @loglines - $limit; + $start = 0 if $start < 0; + map { print "$loglines[$_]\n" } $start .. $#loglines; + + exit 0; +} + +if ( $cmd eq 'recover' ) { + my @f = split /\t/, $loglines[$#loglines]; + die "sorry, the last commit was not a rewind or delete\n" + unless $f[4] eq '+'; + + my($oldsha, $newsha) = @f[5,6]; + if ($newsha =~ /^0+$/) { + print "recovering $repo $ref at $oldsha (was deleted)\n"; + } else { + print "recovering $repo $ref at $oldsha (was forced to $newsha)\n"; + } + chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git"); + require "$ENV{GL_BINDIR}/gitolite.pm" or die "parse gitolite.pm failed\n"; + + my $newsha2 = $newsha; + $newsha2 = '' if $newsha =~ /^0+$/; + system("git", "update-ref", $ref, $oldsha, $newsha2) and + die "repo $repo, update-ref $ref $oldsha $newsha failed...\n"; + &log_it("", "+\t$newsha\t$oldsha\t$repo\t$ref"); +} From b4a3b812a7c5002b91b2ce07aa40767998c7788e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 6 Oct 2010 20:03:50 +0530 Subject: [PATCH 491/850] adc rmrepo: delete reponame from projects.list --- contrib/adc/rmrepo | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contrib/adc/rmrepo b/contrib/adc/rmrepo index 42976f4..21b70e2 100755 --- a/contrib/adc/rmrepo +++ b/contrib/adc/rmrepo @@ -21,3 +21,8 @@ get_rights_and_owner $delete cd $GL_REPO_BASE_ABS rm -rf $delete.git + +cd $HOME +PROJECTS_LIST=$(perl -e 'do ".gitolite.rc"; print $PROJECTS_LIST') +export delete +perl -ni -e 'print unless /^\Q$ENV{delete}.git\E$/' $PROJECTS_LIST From 8a980a60bb650e61394935f77127a28eff016a8a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 6 Oct 2010 20:30:55 +0530 Subject: [PATCH 492/850] tighten up argument handling in ADCs even more More and more people are using ADCs, which I originally wrote just for adventure-loving people ;-) --- src/gitolite.pm | 4 +++- src/gl-auth-command | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gitolite.pm b/src/gitolite.pm index 23f7fe5..80210d5 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -36,6 +36,8 @@ our $REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$); # very simple patter our $USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # very simple pattern # same as REPONAME, but used for wildcard repos, allows some common regex metas our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); +# ADC commands and arguments must match this pattern +our $ADC_CMD_ARGS_PATT=qr(^[0-9a-zA-Z._\@/+-]*$); # these come from the RC file our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST, $GL_ALL_INCLUDES_SPECIAL, $GL_SITE_INFO); @@ -509,7 +511,7 @@ sub parse_acl our $saved_crwu; our (%saved_repos, %saved_groups); - if ($saved_crwu eq "$creator,$readers,$writers,$gl_user") { + if ($saved_crwu and $saved_crwu eq "$creator,$readers,$writers,$gl_user") { %repos = %saved_repos; %groups = %saved_groups; } else { die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED; diff --git a/src/gl-auth-command b/src/gl-auth-command index 15fdfaf..2a6ab02 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -34,7 +34,7 @@ use warnings; # these are set by the "rc" file our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $PROJECTS_LIST, $GL_SLAVE_MODE, $GL_PERFLOGT); # and these are set by gitolite.pm -our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT); +our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT, $ADC_CMD_ARGS_PATT); our %repos; our %groups; our %repo_config; @@ -157,7 +157,7 @@ if ($GL_ADC_PATH and -d $GL_ADC_PATH) { my ($cmd, @args) = split ' ', $ENV{SSH_ORIGINAL_COMMAND}; if (-x "$GL_ADC_PATH/$cmd") { # yes this is rather strict, sorry. - do { die "I don't like $_\n" unless $_ =~ $REPOPATT_PATT } for ($cmd, @args); + do { die "I don't like $_\n" unless $_ =~ $ADC_CMD_ARGS_PATT } for ($cmd, @args); &log_it("$GL_ADC_PATH/$ENV{SSH_ORIGINAL_COMMAND}"); exec("$GL_ADC_PATH/$cmd", @args); } From c40622b302a3b275b23da57665941e28b4e96b12 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 6 Oct 2010 19:56:35 +0530 Subject: [PATCH 493/850] gl-reflog adc: tighten permissions checks - dont do anything if he doesn't even have read access - move the GL_USER check to the right place! (to when you actually will be doing something) That spurious check for GL_USER that we (re)moved would not only have shown an incomplete set of log lines, it would have made the wrong log line look like the "last" one. (No real harm would result, of course, since the update-ref would blow up due to the actual SHA being something other than what it was expecting, but it would be confusing to the user) --- contrib/adc/gl-reflog | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/contrib/adc/gl-reflog b/contrib/adc/gl-reflog index cffee91..aa59e15 100755 --- a/contrib/adc/gl-reflog +++ b/contrib/adc/gl-reflog @@ -9,13 +9,9 @@ use warnings; # -------------------- -# - PROOF OF CONCEPT ONLY -# - NO ARGUMENT OR ERROR CHECKING DONE; DO NOT USE IN PRODUCTION UNTIL THAT IS FIXED - -# -------------------- - # WARNING # - heavily dependent on the gitolite log file format (duh!) +# - cannot recover if some other commits were made after the force push # USAGE # ssh git@server gl-reflog show r1 refs/heads/b1 @@ -37,6 +33,10 @@ use warnings; my($cmd, $repo, $ref, $limit) = @ARGV; $limit ||= 10; +require "$ENV{GL_BINDIR}/gitolite.pm" or die "parse gitolite.pm failed\n"; +my ($perm, $creator, $wild) = &repo_rights($repo); +die "you don't have read access to $repo\n" unless $perm =~ /R/; + my @logfiles = sort glob("$ENV{GL_ADMINDIR}/logs/*"); # TODO figure out how to avoid reading *all* the log files when you really @@ -51,7 +51,6 @@ our @loglines; @f = split /\t/; # field 2 is the userid, 5 is W or +, 6/7 are old/new SHAs # 8 is reponame, 9 is refname (but all those are 1-based) - next unless $f[1] eq $ENV{GL_USER}; next unless $f[3] =~ /^(git-receive-pack|gl-reflog recover) /; next unless $f[8]; next unless $f[7] eq $repo; @@ -70,8 +69,8 @@ if ( $cmd eq 'show' ) { if ( $cmd eq 'recover' ) { my @f = split /\t/, $loglines[$#loglines]; - die "sorry, the last commit was not a rewind or delete\n" - unless $f[4] eq '+'; + die "the last push was not yours\n" unless $f[1] eq $ENV{GL_USER}; + die "the last push was not a rewind or delete\n" unless $f[4] eq '+'; my($oldsha, $newsha) = @f[5,6]; if ($newsha =~ /^0+$/) { @@ -80,7 +79,6 @@ if ( $cmd eq 'recover' ) { print "recovering $repo $ref at $oldsha (was forced to $newsha)\n"; } chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git"); - require "$ENV{GL_BINDIR}/gitolite.pm" or die "parse gitolite.pm failed\n"; my $newsha2 = $newsha; $newsha2 = '' if $newsha =~ /^0+$/; From afbab1dd8d981dc3c71b8054e868f1efb9af8029 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 6 Oct 2010 20:32:48 +0530 Subject: [PATCH 494/850] "sudo" adc The sudo adc should make life easier for any admin wishing to run an adc as some other user. As a result, the rmrepo adc also goes back to its simple roots. Now you just run it via the sudo adc if you (as gitolite admin) needs to rmrepo some user's repo. --- contrib/adc/rmrepo | 11 ----------- contrib/adc/sudo | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 11 deletions(-) create mode 100755 contrib/adc/sudo diff --git a/contrib/adc/rmrepo b/contrib/adc/rmrepo index 21b70e2..d18a612 100755 --- a/contrib/adc/rmrepo +++ b/contrib/adc/rmrepo @@ -6,19 +6,8 @@ delete=$1 get_rights_and_owner $delete -# please choose one alternative and delete the other para - -# alternative 1 -- only creator can delete [ "$owner" = "$GL_USER" ] || die "$delete is not yours to delete!" -# alternative 2 -- creator or anyone with W access to admin repo can delete -[ "$owner" = "$GL_USER" ] || { - # but only if it is wild - [ "$owner" = "" ] && die "$delete is not a wild repo" - get_rights_and_owner gitolite-admin - [ -z "$perm_write" ] && die "$delete is not yours to delete and you're not the admin" -} - cd $GL_REPO_BASE_ABS rm -rf $delete.git diff --git a/contrib/adc/sudo b/contrib/adc/sudo new file mode 100755 index 0000000..818db75 --- /dev/null +++ b/contrib/adc/sudo @@ -0,0 +1,23 @@ +#!/bin/sh + +# this command is pretty cool, even if I may say so myself :) + +# for any ADC that a normal user can run, like + +# ssh git@server adc arguments + +# this adc lets a "super user" (defined as "have write access to the +# gitolite-admin repo"), do this + +# ssh git@server sudo normal_user adc arguments + +. $(dirname $0)/adc.common-functions + +get_rights_and_owner gitolite-admin +[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?" + +user="$1"; shift +cmd="$1"; shift + +GL_USER=$user; export GL_USER +exec $(dirname $0)/$cmd "$@" From ba39d93e28eb125ef2139c93bcc01dc208a8ba3c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 6 Oct 2010 22:57:45 +0530 Subject: [PATCH 495/850] (minor glitch in sudo adc) thanks to jeff for finding it... --- contrib/adc/sudo | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/adc/sudo b/contrib/adc/sudo index 818db75..47d9641 100755 --- a/contrib/adc/sudo +++ b/contrib/adc/sudo @@ -20,4 +20,5 @@ user="$1"; shift cmd="$1"; shift GL_USER=$user; export GL_USER +[ -x $(dirname $0)/$cmd ] || die "no adc called $cmd" exec $(dirname $0)/$cmd "$@" From db0485fa7e92fa2f0164c778df23499d80d4bbb9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 7 Oct 2010 16:36:14 +0530 Subject: [PATCH 496/850] new method for passing usergroup info (warning: minor backward compat breakage) The old method of passing in usergroup info had some problems, which are now fixed. It is also much easier to use now -- no more "wrapper" script, plus it should work identially whether you use sshd or httpd. See doc/big-config.mkd for details on the new method. ---- Notes on problems with the old method: The old method for passing in usergroup info consisted of tacking them on as extra arguments to gl-auth-command, after the username. However, there are some problems with this method. Some actions in gitolite look for permissions for users other than the invoking user. Determining permissions for gitweb and daemon is one. An admin asking for "info" on some other user, is another. However, the list of groups sent in via the command line pertains only to the invoking user, so these actions don't work correctly. They may even pick up the wrong permissions. What it all boils down to is that we need group information for any user dynamically, instead of being passed a (static) list just for the invoking user. --- conf/example.gitolite.rc | 12 +++++++ doc/big-config.mkd | 71 ++++++++++++++++++++++------------------ src/gitolite.pm | 18 +++++++--- src/gl-auth-command | 4 --- 4 files changed, 65 insertions(+), 40 deletions(-) diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index e189595..c563e81 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -273,6 +273,18 @@ $GL_WILDREPOS = 0; # $GL_SITE_INFO = ""; # $GL_SITE_INFO = "XYZ.COM DEVELOPERS: PLEASE SEE http://xyz.com/gitolite/help first"; +# -------------------------------------- +# USERGROUP HANDLING + +# Some sites would like to store group membership outside gitolite, because +# they already have it in (usually) their LDAP server, and it doesn't make +# sense to be forced to duplicate this information. + +# Set the following variable to the name of a script that, given a username as +# argument, will return a list of groups that she is a member of. + +# $GL_GET_MEMBERSHIPS_PGM = "/usr/local/bin/expand-ldap-user-to-groups" + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/doc/big-config.mkd b/doc/big-config.mkd index b2f35b0..c4ea551 100644 --- a/doc/big-config.mkd +++ b/doc/big-config.mkd @@ -6,7 +6,9 @@ In this document: * how do we use it? * other optimisations * what are the downsides? - * (extra coolness) usergroups and LDAP/similar tools + * storing usergroup information outside gitolite (like in LDAP) + * why + * how @@ -190,51 +192,58 @@ subset of the allowed @fragname, which would work normally, do not work now). (If you didn't understand all that, you're probably not using delegation, so feel free to ignore it!) - + -### (extra coolness) usergroups and LDAP/similar tools +### storing usergroup information outside gitolite (like in LDAP) [Please NOTE: this is all about *user* groups, not *repo* groups] -Gitolite now allows usergroup information to be passed in from outside. The -`gl-auth-commmand` can now take an optional list of usergroup names after the -first argument (which is the username). +[WARNING: the earlier method of doing this has been discontinued; please see +the commit message for details] -To understand why this is useful, consider the following: +Gitolite now allows usergroup information to be stored outside its own config +file. We'll see "why" first, then the "how". -Some people have an LDAP-backed ssh daemon (or some other similar mechanism -that can speak "ssh" to the client), with pubkeys stored in LDAP, etc., and -some way (not using `~/.ssh/authorized_keys`) of invoking gitolite. + -Such setups also have "usergroups", and a way to tell, for each user, what -groups he/she is a member of. So let's say "alice" works on projects "foo" -and "bar", while "bob" works on project "bar" and is a member of the -`L3_support` team. +#### why -You can use those group names in the gitolite config file for access control -as "@foo", "@bar", `@L3_support`, etc.; please note the "@" prefix because -gitolite requires it. +Large sites often have LDAP servers that already contain user and group +information, including group membership details. Such sites may prefer that +gitolite just pick up that info instead of having to redundantly put it in +gitolite's config file. -However, that still leaves a wee little inconvenience. You still have to add -lines like this to the gitolite config file: +Consider this example config for one repo: - @foo = alice - @bar = alice bob - @L3_support = bob + repo foo + RW+ = @lead_devs + RW = @devs + R = @interns -You don't need to do that anymore now. Tell your authentication system that, -after authenticating alice, instead of running: +Normally, you would also need to specify: - /some/path/to/gl-auth-command alice + @lead_devs = dilbert alice + @devs = wally + @interns = ashok -it should first find the groups that alice is a member of, and then run: +However, if the corporate LDAP server already tags these people correctly, and +if there is some way of getting that information out **at run time**, that +would be cool. - /some/path/to/gl-auth-command alice foo bar + -That's it. Internally, gitolite will behave as if the config file had also -specified: +#### how - @foo = alice - @bar = alice +All you need is a script that, given a username, queries your LDAP or similar +server, and returns a space-separated list of all the groups she is a member +of. If an invalid user name is sent in, or the user is valid but is not part +of any groups, it should print nothing. + +This script will probably be specific to your site. [**Help wanted**: I don't +know LDAP, so if someone wants to contribute some sample code I'd be happy to +put it in contrib/, with credit of course!] + +Then set the `$GL_GET_MEMBERSHIPS_PGM` variable in the rc file to the full +path to this program, set `$GL_BIG_CONFIG` to 1, and that will be that. [gwd]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gwd diff --git a/src/gitolite.pm b/src/gitolite.pm index 80210d5..d689f88 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -40,7 +40,7 @@ our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$); our $ADC_CMD_ARGS_PATT=qr(^[0-9a-zA-Z._\@/+-]*$); # these come from the RC file -our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST, $GL_ALL_INCLUDES_SPECIAL, $GL_SITE_INFO); +our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST, $GL_ALL_INCLUDES_SPECIAL, $GL_SITE_INFO, $GL_GET_MEMBERSHIPS_PGM); our %repos; our %groups; our %repo_config; @@ -932,6 +932,7 @@ sub special_cmd # - (only for repos) as an indirect wildcard (@g = foo/.*; repo @g). # note: the wildcard stuff does not apply to username memberships +our %extgroups_cache; sub get_memberships { my $base = shift; # reponame or username my $is_repo = shift; # some true value means a repo name has been passed @@ -969,10 +970,17 @@ sub get_memberships { # deal with returning user info first unless ($is_repo) { - # add in group membership info sent in via second and subsequent - # arguments to gl-auth-command; be sure to prefix the "@" sign to each - # of them! - push @ret, map { s/^/@/; $_; } split(' ', $ENV{GL_GROUP_LIST}) if $ENV{GL_GROUP_LIST}; + # bring in group membership info stored externally, by running + # $GL_GET_MEMBERSHIPS_PGM if it is defined + + if ($extgroups_cache{$base}) { + push @ret, @{ $extgroups_cache{$base} }; + } elsif ($GL_GET_MEMBERSHIPS_PGM) { + my @extgroups = map { s/^/@/; $_; } split ' ', `$GL_GET_MEMBERSHIPS_PGM $base`; + $extgroups_cache{$base} = \@extgroups; + push @ret, @extgroups; + } + return (@ret); } diff --git a/src/gl-auth-command b/src/gl-auth-command index 2a6ab02..dabfa71 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -114,10 +114,6 @@ if ($ENV{REQUEST_URI}) { $user=$ENV{GL_USER}=shift; } -# if there are any more arguments, they're a list of group names that the user -# is a member of -$ENV{GL_GROUP_LIST} = join(" ", @ARGV) if @ARGV; - # ---------------------------------------------------------------------------- # logging, timestamp env vars # ---------------------------------------------------------------------------- From 3cf2970e0e19ab89df45843fb871482cb68adb29 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 8 Oct 2010 05:08:13 +0530 Subject: [PATCH 497/850] info leak prevention can be a little more friendly and acquire some German formality while you're about it ;-) --- src/gl-auth-command | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index dabfa71..0ef6ceb 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -242,7 +242,8 @@ if ($perm =~ /C/) { # we know the user and repo; we just need to know what perm he's trying # aa == attempted access my $aa = ($verb =~ $R_COMMANDS ? 'R' : 'W'); -die "$aa access for $repo DENIED to $user\n" unless $perm =~ /$aa/; +die "$aa access for $repo DENIED to $user +(Or there may be no repository at the given path. Did you spell it correctly?)\n" unless $perm =~ /$aa/; # ---------------------------------------------------------------------------- # over to git now From 99d6b4875c9edf33d81dcc4dc64a7965b79aa4d3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 16 Oct 2010 12:22:47 +0530 Subject: [PATCH 498/850] adc su-expand to allow admin to run 'expand' on other users --- contrib/adc/su-expand | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100755 contrib/adc/su-expand diff --git a/contrib/adc/su-expand b/contrib/adc/su-expand new file mode 100755 index 0000000..95234e9 --- /dev/null +++ b/contrib/adc/su-expand @@ -0,0 +1,56 @@ +#!/bin/sh + +# adc for someone with admin privs to invoke "expand" on other users. + +# This doc block as "WHY", "HOW", and "CAVEATS" sections; I mention that only +# for those people with very small xterms who may miss the CAVEATS section +# otherwise...! + +# WHY +# === + + # ...because info has it, and expand doesn't :-) + + # (Possible question: Why not add that capability to expand instead to be + # consistent? Probable answer: how about we go the other way and make + # "info" also work like this, and remove that code from core? I like + # having less less code in core!) + +# HOW +# === + + # ssh gitolite su-expand . user1 user2 user3 + + # note that the first argument is still treated as a "pattern", so at + # least put in a "." for a catch-all pattern. Also see caveats for + # patterns below. + + # This will output the same thing as "ssh git@server expand ." performed + # by the individual users + +# CAVEATS +# ======= + +# (1) LIMITS TO THE PATTERN + + # Due to this being an ADC, and ADC arguments being very, Very, VERY, + # stricly limited, you won't be able to use any regex meta characters + # expect ".". I'll worry about changing it if enough people complain. + +# (2) NAME OF THIS SCRIPT + + # please name this script something other than "expand", because we can't + # have name clashes between internal commands (info, expand, + # (get|set)(perms|desc), etc) and admin-defined (external) commands. I'm + # calling it su-expand; feel free to change it. + +. $(dirname $0)/adc.common-functions + +get_rights_and_owner gitolite-admin +[ -z "$perm_write" ] && die "just *what* are you trying to pull here, $GL_USER?" +pat="$1"; shift + +for user +do + SSH_ORIGINAL_COMMAND="expand $pat" $GL_BINDIR/gl-auth-command $user +done From d1515ea8d8b014f88af50c746469d80c0a95aab2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 16 Oct 2010 14:46:00 +0530 Subject: [PATCH 499/850] update CHANGELOG --- doc/CHANGELOG | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 51459a2..69b8b56 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,6 +2,15 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] + - v1.5.6 + + - new method for passing usergroup info (minor backward compat breakage); + see commit message and doc/big-config.mkd for more on this + + - added "sudo" (adc) + - added "gl-reflog" (adc) to get a fake reflog from the server! + - added support for a post-repo-create hook called "gl-post-init" + - (BIG ONE!) SMART HTTP SUPPORT! - @all can now include gitweb/daemon also (default, not include) From 5fd06036edf408f81dbc33dff869f732c0b057e2 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 28 Feb 2012 22:15:45 +0530 Subject: [PATCH 500/850] empty From 60e190215e5e6defe593df8b3eb2e7d3bd409f46 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 8 Mar 2012 13:30:13 +0530 Subject: [PATCH 501/850] very basic, usable, first cut done - sausage making hidden - lots of important features missing --- Gitolite/Commands/QueryRc.pm | 81 +++++ Gitolite/Commands/Setup.pm | 161 +++++++++ Gitolite/Common.pm | 175 ++++++++++ Gitolite/Conf.pm | 183 ++++++++++ Gitolite/Conf/Load.pm | 169 ++++++++++ Gitolite/Conf/Store.pm | 356 ++++++++++++++++++++ Gitolite/Conf/Sugar.pm | 82 +++++ Gitolite/Hooks/PostUpdate.pm | 69 ++++ Gitolite/Hooks/Update.pm | 114 +++++++ Gitolite/Rc.pm | 112 +++++++ Gitolite/Test.pm | 34 ++ Gitolite/Test/Tsh.pm | 624 +++++++++++++++++++++++++++++++++++ g3-info | 35 ++ g3-install | 20 ++ gitolite | 54 +++ gitolite-shell | 57 ++++ src/gitolite | 106 ++++++ t/gitolite-receive-pack | 12 + t/gitolite-upload-pack | 12 + t/glt | 28 ++ t/t01-basic | 110 ++++++ 21 files changed, 2594 insertions(+) create mode 100644 Gitolite/Commands/QueryRc.pm create mode 100644 Gitolite/Commands/Setup.pm create mode 100644 Gitolite/Common.pm create mode 100644 Gitolite/Conf.pm create mode 100644 Gitolite/Conf/Load.pm create mode 100644 Gitolite/Conf/Store.pm create mode 100644 Gitolite/Conf/Sugar.pm create mode 100644 Gitolite/Hooks/PostUpdate.pm create mode 100644 Gitolite/Hooks/Update.pm create mode 100644 Gitolite/Rc.pm create mode 100644 Gitolite/Test.pm create mode 100644 Gitolite/Test/Tsh.pm create mode 100755 g3-info create mode 100755 g3-install create mode 100755 gitolite create mode 100755 gitolite-shell create mode 100755 src/gitolite create mode 100755 t/gitolite-receive-pack create mode 100755 t/gitolite-upload-pack create mode 100755 t/glt create mode 100755 t/t01-basic diff --git a/Gitolite/Commands/QueryRc.pm b/Gitolite/Commands/QueryRc.pm new file mode 100644 index 0000000..a36e4bd --- /dev/null +++ b/Gitolite/Commands/QueryRc.pm @@ -0,0 +1,81 @@ +package Gitolite::Commands::QueryRc; + +# implements 'gitolite query-rc' +# ---------------------------------------------------------------------- + +=for usage + +Usage: gitolite query-rc -a + gitolite query-rc + +Example: + + gitolite query-rc GL_ADMIN_BASE GL_UMASK + # prints "/home/git/.gitolite0077" or similar + + gitolite query-rc -a + # prints all known variables and values, one per line +=cut + +# ---------------------------------------------------------------------- + +@EXPORT = qw( + query_rc +); + +use Exporter 'import'; +use Getopt::Long; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +my $all = 0; + +# ---------------------------------------------------------------------- + +sub query_rc { + trace( 1, "rc file not found; default should be " . glrc_default_filename() ) if not glrc_filename(); + + my @vars = args(); + + no strict 'refs'; + + if ( $vars[0] eq '-a' ) { + for my $e (@Gitolite::Rc::EXPORT) { + # perl-ism warning: if you don't do this the implicit aliasing + # screws up Rc's EXPORT list + my $v = $e; + # we stop on the first non-$ var + last unless $v =~ s/^\$//; + print "$v=" . ( defined($$v) ? $$v : 'undef' ) . "\n"; + } + } + + our $GL_BINDIR = $ENV{GL_BINDIR}; + + print join( "\t", map { $$_ } grep { $$_ } @vars ) . "\n" if @vars; +} + +# ---------------------------------------------------------------------- + +sub args { + my $help = 0; + + GetOptions( + 'all|a' => \$all, + 'help|h' => \$help, + ) or usage(); + + usage("'-a' cannot be combined with other arguments") if $all and @ARGV; + return '-a' if $all; + usage() if not @ARGV or $help; + return @ARGV; +} + +1; diff --git a/Gitolite/Commands/Setup.pm b/Gitolite/Commands/Setup.pm new file mode 100644 index 0000000..aa312ad --- /dev/null +++ b/Gitolite/Commands/Setup.pm @@ -0,0 +1,161 @@ +package Gitolite::Commands::Setup; + +# implements 'gitolite setup' +# ---------------------------------------------------------------------- + +=for usage +Usage: gitolite setup [] + + + -a, --admin admin user name + -pk --pubkey pubkey file name + -f, --fixup-hooks fixup hooks + +First run: + -a required + -pk required for ssh mode install + +Later runs: + no options required; but '-f' can be specified for clarity +=cut + +# ---------------------------------------------------------------------- + +@EXPORT = qw( + setup +); + +use Exporter 'import'; +use Getopt::Long; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; +use Gitolite::Conf::Store; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +sub setup { + my ( $admin, $pubkey, $argv ) = args(); + # first time + if ( first_run() ) { + trace( 1, "..should happen only on first run" ); + setup_glrc(); + setup_gladmin( $admin, $pubkey, $argv ); + } + + system("$ENV{GL_BINDIR}/gitolite compile"); + + hook_repos(); # all of them, just to be sure +} + +# ---------------------------------------------------------------------- + +sub first_run { + # if the rc file could not be found, it's *definitely* a first run! + return not glrc_filename(); +} + +sub args { + my $admin = ''; + my $pubkey = ''; + my $fixup = 0; + my $help = 0; + my $argv = join( " ", @ARGV ); + + GetOptions( + 'admin|a=s' => \$admin, + 'pubkey|pk=s' => \$pubkey, + 'fixup-hooks|f' => \$fixup, + 'help|h' => \$help, + ) or usage(); + + usage() if $help; + usage("first run requires '-a'") if first_run() and not($admin); + _warn("not setting up ssh...") if first_run() and $admin and not $pubkey; + _warn("first run, ignoring '-f'...") if first_run() and $fixup; + _warn("not first run, ignoring '-a' / '-pk'...") if not first_run() and ( $admin or $pubkey ); + + if ($pubkey) { + $pubkey =~ /\.pub$/ or _die "$pubkey name does not end in .pub"; + tsh_try("cat $pubkey") or _die "$pubkey not a readable file"; + tsh_lines() == 1 or _die "$pubkey must have exactly one line"; + tsh_try("ssh-keygen -l -f $pubkey") or _die "$pubkey does not seem to be a valid ssh pubkey file"; + } + + return ( $admin || '', $pubkey || '', $argv ); +} + +sub setup_glrc { + trace(1); + _print( glrc_default_filename(), glrc_default_text() ); +} + +sub setup_gladmin { + my ( $admin, $pubkey, $argv ) = @_; + trace( 1, $admin ); + + # reminder: 'admin files' are in ~/.gitolite, 'admin repo' is + # $GL_REPO_BASE/gitolite-admin.git + + # grab the pubkey content before we chdir() away + + my $pubkey_content = ''; + if ($pubkey) { + $pubkey_content = slurp($pubkey); + $pubkey =~ s(.*/)(); # basename + } + + # set up the admin files in admin-base + + _mkdir($GL_ADMIN_BASE); + _chdir($GL_ADMIN_BASE); + + _mkdir("conf"); + my $conf; + { + local $/ = undef; + $conf = ; + } + $conf =~ s/%ADMIN/$admin/g; + + _print( "conf/gitolite.conf", $conf ); + + if ($pubkey) { + _mkdir("keydir"); + _print( "keydir/$pubkey", $pubkey_content ); + } + + # set up the admin repo in repo-base + + _chdir(); + _mkdir($GL_REPO_BASE); + _chdir($GL_REPO_BASE); + + new_repo("gitolite-admin"); + + # commit the admin files to the admin repo + + $ENV{GIT_WORK_TREE} = $GL_ADMIN_BASE; + _chdir("$GL_REPO_BASE/gitolite-admin.git"); + system("git add conf/gitolite.conf"); + system("git add keydir") if $pubkey; + tsh_try("git config --get user.email") or tsh_run( "git config user.email $ENV{USER}\@" . `hostname` ); + tsh_try("git config --get user.name") or tsh_run( "git config user.name '$ENV{USER} on '" . `hostname` ); + tsh_try("git diff --cached --quiet") + or tsh_try("git commit -am 'gl-setup $argv'") + or die "setup failed to commit to the admin repo"; + delete $ENV{GIT_WORK_TREE}; +} + +1; + +__DATA__ +repo gitolite-admin + RW+ = %ADMIN + +repo testing + RW+ = @all diff --git a/Gitolite/Common.pm b/Gitolite/Common.pm new file mode 100644 index 0000000..e5d492a --- /dev/null +++ b/Gitolite/Common.pm @@ -0,0 +1,175 @@ +package Gitolite::Common; + +# common (non-gitolite-specific) functions +# ---------------------------------------------------------------------- + +#<<< +@EXPORT = qw( + print2 dbg _mkdir _open ln_sf tsh_rc sort_u + say _warn _chdir _print tsh_text + say2 _die slurp tsh_lines + trace tsh_try + usage tsh_run +); +#>>> +use Exporter 'import'; +use File::Path qw(mkpath); +use Carp qw(carp cluck croak confess); + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +sub print2 { + local $/ = "\n"; + print STDERR @_; +} + +sub say { + local $/ = "\n"; + print @_, "\n"; +} + +sub say2 { + local $/ = "\n"; + print STDERR @_, "\n"; +} + +sub trace { + return unless defined( $ENV{D} ); + + my $level = shift; + my $args = ''; $args = join( ", ", @_ ) if @_; + my $sub = ( caller 1 )[3] || ''; $sub =~ s/.*://; $sub .= ' ' x ( 32 - length($sub) ); + say2 "TRACE $level $sub", $args if $ENV{D} >= $level; +} + +sub dbg { + use Data::Dumper; + return unless defined( $ENV{D} ); + for my $i (@_) { + print STDERR "DBG: " . Dumper($i); + } +} + +sub _warn { + if ( $ENV{D} and $ENV{D} >= 3 ) { + cluck "WARNING: ", @_, "\n"; + } elsif ( defined( $ENV{D} ) ) { + carp "WARNING: ", @_, "\n"; + } else { + warn "WARNING: ", @_, "\n"; + } +} + +sub _die { + if ( $ENV{D} and $ENV{D} >= 3 ) { + confess "FATAL: " . join( ",", @_ ) . "\n" if defined( $ENV{D} ); + } elsif ( defined( $ENV{D} ) ) { + croak "FATAL: " . join( ",", @_ ) . "\n"; + } else { + die "FATAL: " . join( ",", @_ ) . "\n"; + } +} + +sub usage { + _warn(shift) if @_; + my $scriptname = ( caller() )[1]; + my $script = slurp($scriptname); + $script =~ /^=for usage(.*?)^=cut/sm; + say2( $1 ? $1 : "...no usage message in $scriptname" ); + exit 1; +} + +sub _mkdir { + # it's not an error if the directory exists, but it is an error if it + # doesn't exist and we can't create it + my $dir = shift; + my $perm = shift; # optional + return if -d $dir; + mkpath($dir); + chmod $perm, $dir if $perm; + return 1; +} + +sub _chdir { + chdir( $_[0] || $ENV{HOME} ) or _die "chdir $_[0] failed: $!\n"; +} + +sub _open { + open( my $fh, $_[0], $_[1] ) or _die "open $_[1] failed: $!\n"; + return $fh; +} + +sub _print { + my ( $file, @text ) = @_; + my $fh = _open( ">", "$file.$$" ); + print $fh @text; + close($fh) or _die "close $file failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; + my $oldmode = ( ( stat $file )[2] ); + rename "$file.$$", $file; + chmod $oldmode, $file if $oldmode; +} + +sub slurp { + local $/ = undef; + my $fh = _open( "<", $_[0] ); + return <$fh>; +} + +sub dos2unix { + # WARNING: when calling this, make sure you supply a list context + s/\r\n/\n/g for @_; + return @_; +} + +sub ln_sf { + trace( 4, @_ ); + my ( $srcdir, $glob, $dstdir ) = @_; + for my $hook ( glob("$srcdir/$glob") ) { + $hook =~ s/$srcdir\///; + unlink "$dstdir/$hook"; + symlink "$srcdir/$hook", "$dstdir/$hook" or croak "could not symlink $srcdir/$hook to $dstdir\n"; + } +} + +sub sort_u { + my %uniq; + my $listref = shift; + return [] unless @{ $listref }; + undef @uniq{ @{ $listref } }; # expect a listref + my @sort_u = sort keys %uniq; + return \@sort_u; +} +# ---------------------------------------------------------------------- + +# bare-minimum subset of 'Tsh' (see github.com/sitaramc/tsh) +{ + my ( $rc, $text ); + sub tsh_rc { return $rc || 0; } + sub tsh_text { return $text || ''; } + sub tsh_lines { return split /\n/, $text; } + + sub tsh_try { + my $cmd = shift; die "try: expects only one argument" if @_; + $text = `( $cmd ) 2>&1; echo -n RC=\$?`; + if ( $text =~ s/RC=(\d+)$// ) { + $rc = $1; + trace( 4, $text ); + return ( not $rc ); + } + die "couldnt find RC= in result; this should not happen:\n$text\n\n...\n"; + } + + sub tsh_run { + open( my $fh, "-|", @_ ) or die "popen failed: $!"; + local $/ = undef; $text = <$fh>; + close $fh; warn "pclose failed: $!" if $!; + $rc = ( $? >> 8 ); + trace( 4, $text ); + return $text; + } +} + +1; diff --git a/Gitolite/Conf.pm b/Gitolite/Conf.pm new file mode 100644 index 0000000..8f7e111 --- /dev/null +++ b/Gitolite/Conf.pm @@ -0,0 +1,183 @@ +package Gitolite::Conf; + +# explode/parse a conf file +# ---------------------------------------------------------------------- + +@EXPORT = qw( + compile + explode + parse +); + +use Exporter 'import'; +use Getopt::Long; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Common; +use Gitolite::Rc; +use Gitolite::Conf::Sugar; +use Gitolite::Conf::Store; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +# 'seen' for include/subconf files +my %included = (); +# 'seen' for group names on LHS +my %prefixed_groupname = (); + +# ---------------------------------------------------------------------- + +sub compile { + trace(3); + # XXX assume we're in admin-base/conf + + _chdir($GL_ADMIN_BASE); + _chdir("conf"); + + explode( 'gitolite.conf', 'master', \&parse ); + + # the order matters; new repos should be created first, to give store a + # place to put the individual gl-conf files + new_repos(); + store(); +} + +sub explode { + trace( 4, @_ ); + my ( $file, $subconf, $parser ) = @_; + + # $parser is a ref to a callback; if not supplied we just print + $parser ||= sub { print shift, "\n"; }; + + # seed the 'seen' list if it's empty + $included{ device_inode("conf/gitolite.conf") }++ unless %included; + + my $fh = _open( "<", $file ); + my @fh = <$fh>; + my @lines = macro_expand( "# BEGIN $file\n", @fh, "# END $file\n" ); + my $line; + while (@lines) { + $line = shift @lines; + + $line = cleanup_conf_line($line); + next unless $line =~ /\S/; + + $line = prefix_groupnames( $line, $subconf ) if $subconf ne 'master'; + + if ( $line =~ /^(include|subconf) "(.+)"$/ or $line =~ /^(include|subconf) '(.+)'$/ ) { + incsub( $1, $2, $subconf, $parser ); + } else { + # normal line, send it to the callback function + $parser->($line); + } + } +} + +sub parse { + trace( 4, @_ ); + my $line = shift; + + # user or repo groups + if ( $line =~ /^(@\S+) = (.*)/ ) { + add_to_group( $1, split( ' ', $2 ) ); + } elsif ( $line =~ /^repo (.*)/ ) { + set_repolist( split( ' ', $1 ) ); + } elsif ( $line =~ /^(-|C|R|RW\+?(?:C?D?|D?C?)M?) (.* )?= (.+)/ ) { + my $perm = $1; + my @refs = parse_refs( $2 || '' ); + my @users = parse_users($3); + + # XXX what do we do? s/\bCREAT[EO]R\b/~\$creator/g for @users; + + for my $ref (@refs) { + for my $user (@users) { + add_rule( $perm, $ref, $user ); + } + } + } elsif ( $line =~ /^config (.+) = ?(.*)/ ) { + my ( $key, $value ) = ( $1, $2 ); + my @validkeys = split( ' ', ( $GL_GITCONFIG_KEYS || '' ) ); + push @validkeys, "gitolite-options\\..*"; + my @matched = grep { $key =~ /^$_$/ } @validkeys; + # XXX move this also to add_config: _die "git config $key not allowed\ncheck GL_GITCONFIG_KEYS in the rc file for how to allow it" if (@matched < 1); + # XXX both $key and $value must satisfy a liberal but secure pattern + add_config( 1, $key, $value ); + } elsif ( $line =~ /^subconf (\S+)$/ ) { + set_subconf($1); + } else { + _warn "?? $line"; + } +} + +# ---------------------------------------------------------------------- + +sub incsub { + my $is_subconf = ( +shift eq 'subconf' ); + my ( $include_glob, $subconf, $parser ) = @_; + + _die "subconf $subconf attempting to run 'subconf'\n" if $is_subconf and $subconf ne 'master'; + + # XXX move this to Macros... substitute HOSTNAME word if GL_HOSTNAME defined, otherwise leave as is + # $include_glob =~ s/\bHOSTNAME\b/$GL_HOSTNAME/ if $GL_HOSTNAME; + + # XXX g2 diff: include glob is *implicitly* from $GL_ADMIN_BASE/conf, not *explicitly* + # for my $file (glob($include_glob =~ m(^/) ? $include_glob : "$GL_ADMIN_BASE/conf/$include_glob")) { + + trace( 3, $is_subconf, $include_glob ); + + for my $file ( glob($include_glob) ) { + _warn("included file not found: '$file'"), next unless -f $file; + _die "invalid include/subconf filename $file" unless $file =~ m(([^/]+).conf$); + my $basename = $1; + + next if already_included($file); + + if ($is_subconf) { + $parser->("subconf $basename"); + explode( $file, $basename, $parser ); + $parser->("subconf $subconf"); + # XXX g2 delegaton compat: deal with this: $subconf_seen++; + } else { + explode( $file, $subconf, $parser ); + } + } +} + +sub prefix_groupnames { + my ( $line, $subconf ) = @_; + + my $lhs = ''; + # save 'foo' if it's an '@foo = list' line + $lhs = $1 if $line =~ /^@(\S+) = /; + # prefix all @groups in the line + $line =~ s/(^| )(@\S+)(?= |$)/ $1 . ($prefixed_groupname{$subconf}{$2} || $2) /ge; + # now prefix the LHS and store it if needed + if ($lhs) { + $line =~ s/^@\S+ = /"\@$subconf.$lhs = "/e; + trace( 3, "prefixed_groupname.$subconf.\@$lhs = \@$subconf.$lhs" ); + } + + return $line; +} + +sub already_included { + my $file = shift; + + my $file_id = device_inode($file); + return 0 unless $included{$file_id}++; + + _warn("$file already included"); + trace( 3, "$file already included" ); + return 1; +} + +sub device_inode { + my $file = shift; + trace( 3, $file, ( stat $file )[ 0, 1 ] ); + return join( "/", ( stat $file )[ 0, 1 ] ); +} + +1; diff --git a/Gitolite/Conf/Load.pm b/Gitolite/Conf/Load.pm new file mode 100644 index 0000000..db11679 --- /dev/null +++ b/Gitolite/Conf/Load.pm @@ -0,0 +1,169 @@ +package Gitolite::Conf::Load; + +# load conf data from stored files +# ---------------------------------------------------------------------- + +@EXPORT = qw( + load + access +); + +use Exporter 'import'; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Common; +use Gitolite::Rc; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +my $subconf = 'master'; + +# our variables, because they get loaded by a 'do' +our $data_version = ''; +our %repos; +our %one_repo; +our %groups; +our %configs; +our %one_config; +our %split_conf; + +# helps maintain the "cache" in both "load_common" and "load_1" +my $last_repo = ''; + +# ---------------------------------------------------------------------- + +{ + my $loaded_repo = ''; + + sub load { + my $repo = shift or _die "load() needs a reponame"; + trace( 4, "$repo" ); + if ( $repo ne $loaded_repo ) { + trace( 3, "loading $repo..." ); + _chdir("$GL_ADMIN_BASE"); load_common(); + _chdir("$GL_REPO_BASE"); load_1($repo); + $loaded_repo = $repo; + } + } +} + +sub access { + my ( $repo, $user, $aa, $ref ) = @_; + trace( 3, "repo=$repo, user=$user, aa=$aa, ref=$ref" ); + load($repo); + + my @rules = rules( $repo, $user ); + trace( 3, scalar(@rules) . " rules found" ); + for my $r (@rules) { + my $perm = $r->[1]; + my $refex = $r->[2]; + trace( 4, "perm=$perm, refex=$refex" ); + + # skip 'deny' rules if the ref is not (yet) known + next if $perm eq '-' and $ref eq 'unknown'; + + # rule matches if ref matches or ref is unknown (see gitolite-shell) + next unless $ref =~ /^$refex/ or $ref eq 'unknown'; + + trace( 3, "DENIED by $refex" ) if $perm eq '-'; + return "DENIED: $aa access to $repo by $user (rule: $refex)" if $perm eq '-'; + + # $perm can be RW\+?(C|D|CD|DC)?M?. $aa can be W, +, C or D, or + # any of these followed by "M". + ( my $aaq = $aa ) =~ s/\+/\\+/; + $aaq =~ s/M/.*M/; + # as far as *this* ref is concerned we're ok + return $refex if ( $perm =~ /$aaq/ ); + } + trace( 3, "DENIED by fallthru" ); + return "DENIED: $aa access to $repo by $user (fallthru)"; +} + +# ---------------------------------------------------------------------- + +sub load_common { + + # we take an unusual approach to caching this function! + # (requires that first call to load_common is before first call to load_1) + if ( $last_repo and $split_conf{$last_repo} ) { + delete $repos{$last_repo}; + delete $configs{$last_repo}; + return; + } + + trace(4); + my $cc = "conf/gitolite.conf-compiled.pm"; + + _die "parse $cc failed: " . ( $! or $@ ) unless do $cc; + + if ( data_version_mismatch() ) { + system("gitolite setup"); + _die "parse $cc failed: " . ( $! or $@ ) unless do $cc; + _die "data version update failed; this is serious" if data_version_mismatch(); + } +} + +sub load_1 { + my $repo = shift; + trace( 4, $repo ); + + if ( $repo eq $last_repo ) { + $repos{$repo} = $one_repo{$repo}; + $configs{$repo} = $one_config{$repo} if $one_config{$repo}; + return; + } + + if ( -f "$repo.git/gl-conf" ) { + _die "split conf not set, gl-conf present for $repo" if not $split_conf{$repo}; + + my $cc = "$repo.git/gl-conf"; + _die "parse $cc failed: " . ( $! or $@ ) unless do $cc; + + $last_repo = $repo; + $repos{$repo} = $one_repo{$repo}; + $configs{$repo} = $one_config{$repo} if $one_config{$repo}; + } else { + _die "split conf set, gl-conf not present for $repo" if $split_conf{$repo}; + } +} + +sub rules { + my ( $repo, $user ) = @_; + trace( 4, "repo=$repo, user=$user" ); + my @rules = (); + + my @repos = memberships($repo); + my @users = memberships($user); + trace( 4, "memberships: " . scalar(@repos) . " repos and " . scalar(@users) . " users found" ); + + for my $r (@repos) { + for my $u (@users) { + push @rules, @{ $repos{$r}{$u} } if exists $repos{$r}{$u}; + } + } + + # dbg("before sorting rules:", \@rules); + @rules = sort { $a->[0] <=> $b->[0] } @rules; + # dbg("after sorting rules:", \@rules); + + return @rules; +} + +sub memberships { + my $item = shift; + + my @ret = ( $item, '@all' ); + push @ret, @{ $groups{$item} } if $groups{$item}; + + return @ret; +} + +sub data_version_mismatch { + return $data_version ne $current_data_version; +} + +1; + diff --git a/Gitolite/Conf/Store.pm b/Gitolite/Conf/Store.pm new file mode 100644 index 0000000..69056a0 --- /dev/null +++ b/Gitolite/Conf/Store.pm @@ -0,0 +1,356 @@ +package Gitolite::Conf::Store; + +# receive parsed conf data and store it +# ---------------------------------------------------------------------- + +@EXPORT = qw( + add_to_group + expand_list + set_repolist + parse_refs + parse_users + add_rule + set_subconf + new_repos + new_repo + hook_repos + store +); + +use Exporter 'import'; +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Common; +use Gitolite::Rc; +use Gitolite::Hooks::Update; +use Gitolite::Hooks::PostUpdate; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +my %repos; +my %groups; +my %configs; +my %split_conf; + +my @repolist; # current repo list; reset on each 'repo ...' line +my $subconf = 'master'; +my $ruleseq = 0; +my %ignored; +# XXX you still have to "warn" if this has any entries + +# ---------------------------------------------------------------------- + +sub add_to_group { + my ( $lhs, @rhs ) = @_; + _die "bad group '$lhs'" unless $lhs =~ $REPONAME_PATT; + + # store the group association, but overload it to keep track of when + # the group was *first* created by using $subconf as the *value* + do { $groups{$lhs}{$_} ||= $subconf } + for ( expand_list(@rhs) ); + + # create the group hash even if empty + $groups{$lhs} = {} unless $groups{$lhs}; +} + +sub expand_list { + my @list = @_; + my @new_list = (); + + for my $item (@list) { + if ( $item =~ /^@/ and $item ne '@all' ) # nested group + { + _die "undefined group $item" unless $groups{$item}; + # add those names to the list + push @new_list, sort keys %{ $groups{$item} }; + } else { + push @new_list, $item; + } + } + + return @new_list; +} + +sub set_repolist { + @repolist = @_; + + # ...sanity checks + for (@repolist) { + _warn "explicit '.git' extension ignored for $_.git" if s/\.git$//; + _die "bad reponame '$_'" if $_ !~ $REPOPATT_PATT; + } + # XXX -- how do we deal with this? s/\bCREAT[EO]R\b/\$creator/g for @{ $repos_p }; +} + +sub parse_refs { + my $refs = shift; + my @refs; @refs = split( ' ', $refs ) if $refs; + @refs = expand_list(@refs); + + # if no ref is given, this PERM applies to all refs + @refs = qw(refs/.*) unless @refs; + + # fully qualify refs that dont start with "refs/" or "NAME/" or "VREF/"; + # prefix them with "refs/heads/" + @refs = map { m(^(refs|NAME|VREF)/) or s(^)(refs/heads/); $_ } @refs; + # XXX what do we do? @refs = map { s(/USER/)(/\$gl_user/); $_ } @refs; + + return @refs; +} + +sub parse_users { + my $users = shift; + my @users = split ' ', $users; + do { _die "bad username '$_'" unless $_ =~ $USERNAME_PATT } + for @users; + + return @users; +} + +sub add_rule { + my ( $perm, $ref, $user ) = @_; + + $ruleseq++; + for my $repo (@repolist) { + if ( check_subconf_repo_disallowed( $subconf, $repo ) ) { + my $repo = $repo; + $repo =~ s/^\@$subconf\./locally modified \@/; + $ignored{$subconf}{$repo} = 1; + next; + } + + push @{ $repos{$repo}{$user} }, [ $ruleseq, $perm, $ref ]; + + # XXX g2 diff: we're not doing a lint check for usernames versus pubkeys; + # maybe we can add that later + + # XXX to do: C/R/W, then CREATE_IS_C, etc + # XXX to do: also NAME_LIMITS + # XXX and hacks like $creator -> "$creatror - wild" + + # XXX consider if you want to use rurp_seen; initially no + } +} + +sub set_subconf { + $subconf = shift; + trace( 1, $subconf ); +} + +sub new_repos { + trace(3); + _chdir($GL_REPO_BASE); + + # normal repos + my @repos = grep { $_ =~ $REPONAME_PATT and not /^@/ } sort keys %repos; + # add in members of repo groups + map { push @repos, keys %{ $groups{$_} } } grep { /^@/ } keys %repos; + + for my $repo ( @{ sort_u(\@repos) } ) { + next unless $repo =~ $REPONAME_PATT; # skip repo patterns + next if $repo =~ m(^\@|EXTCMD/); # skip groups and fake repos + + # XXX how do we deal with GL_NO_CREATE_REPOS? + new_repo($repo) if not -d "$repo.git"; + } +} + +sub new_repo { + my $repo = shift; + trace( 4, $repo ); + + # XXX ignoring UMASK for now + + _mkdir("$repo.git"); + _chdir("$repo.git"); + system("git init --bare >&2"); + _chdir($GL_REPO_BASE); + hook_1($repo); + + # XXX ignoring creator for now + # XXX ignoring gl-post-init for now +} + +sub hook_repos { + trace(3); + # all repos, all hooks + _chdir($GL_REPO_BASE); + + # XXX g2 diff: we now don't care if it's a symlink -- it's upto the admin + # on the server to make sure things are kosher + for my $repo (`find . -name "*.git" -prune`) { + chomp($repo); + $repo =~ s/\.git$//; + hook_1($repo); + } +} + +sub store { + trace(3); + + # first write out the ones for the physical repos + my @phy_repos = list_physical_repos(1); + + _chdir($GL_REPO_BASE); + for my $repo (@phy_repos) { + store_1($repo); + } + + _chdir($GL_ADMIN_BASE); + store_common(); +} + +# ---------------------------------------------------------------------- + +sub check_subconf_repo_disallowed { + # trying to set access for $repo (='foo')... + my ( $subconf, $repo ) = @_; + + # processing the master config, not a subconf + return 0 if $subconf eq 'master'; + # subconf is also called 'foo' (you're allowed to have a + # subconf that is only concerned with one repo) + return 0 if $subconf eq $repo; + # same thing in big-config-land; foo is just @foo now + return 0 if ( "\@$subconf" eq $repo ); + my @matched = grep { $repo =~ /^$_$/ } + grep { $groups{"\@$subconf"}{$_} eq 'master' } + sort keys %{ $groups{"\@$subconf"} }; + return 0 if @matched > 0; + + trace( 3, "disallowed: $subconf for $repo" ); + return 1; +} + +{ + my @phy_repos = (); + + sub list_physical_repos { + trace(3); + _chdir($GL_REPO_BASE); + + # use cached value only if it exists *and* no arg was received (i.e., + # receiving *any* arg invalidates cache) + return @phy_repos if ( @phy_repos and not @_ ); + + for my $repo (`find . -name "*.git" -prune`) { + chomp($repo); + $repo =~ s(\./(.*)\.git$)($1); + push @phy_repos, $repo; + } + return @phy_repos; + } +} + +sub store_1 { + # warning: writes and *deletes* it from %repos and %configs + my ($repo) = shift; + trace( 4, $repo ); + return unless $repos{$repo} and -d "$repo.git"; + + my ( %one_repo, %one_config ); + + open( my $compiled_fh, ">", "$repo.git/gl-conf" ) or return; + + $one_repo{$repo} = $repos{$repo}; + delete $repos{$repo}; + my $dumped_data = Data::Dumper->Dump( [ \%one_repo ], [qw(*one_repo)] ); + + if ( $configs{$repo} ) { + $one_config{$repo} = $configs{$repo}; + delete $configs{$repo}; + $dumped_data .= Data::Dumper->Dump( [ \%one_config ], [qw(*one_config)] ); + } + + # XXX deal with this better now + # $dumped_data =~ s/'(?=[^']*\$(?:creator|gl_user))~?(.*?)'/"$1"/g; + print $compiled_fh $dumped_data; + close $compiled_fh; + + $split_conf{$repo} = 1; +} + +sub store_common { + trace(4); + my $cc = "conf/gitolite.conf-compiled.pm"; + my $compiled_fh = _open( ">", "$cc.new" ); + + my $data_version = $current_data_version; + trace( 1, "data_version = $data_version" ); + print $compiled_fh Data::Dumper->Dump( [$data_version], [qw(*data_version)] ); + + my $dumped_data = Data::Dumper->Dump( [ \%repos ], [qw(*repos)] ); + $dumped_data .= Data::Dumper->Dump( [ \%configs ], [qw(*configs)] ) if %configs; + + # XXX and again... + # XXX $dumped_data =~ s/'(?=[^']*\$(?:creator|gl_user))~?(.*?)'/"$1"/g; + + print $compiled_fh $dumped_data; + + if (%groups) { + my %groups = %{ inside_out( \%groups ) }; + $dumped_data = Data::Dumper->Dump( [ \%groups ], [qw(*groups)] ); + # XXX $dumped_data =~ s/\bCREAT[EO]R\b/\$creator/g; + # XXX $dumped_data =~ s/'(?=[^']*\$(?:creator|gl_user))~?(.*?)'/"$1"/g; + print $compiled_fh $dumped_data; + } + print $compiled_fh Data::Dumper->Dump( [ \%split_conf ], [qw(*split_conf)] ) if %split_conf; + + close $compiled_fh or _die "close compiled-conf failed: $!\n"; + rename "$cc.new", $cc; +} + +{ + my $hook_reset = 0; + + sub hook_1 { + my $repo = shift; + trace( 4, $repo ); + + # reset the gitolite supplied hooks, in case someone fiddled with + # them, but only once per run + if ( not $hook_reset ) { + _mkdir("$GL_ADMIN_BASE/hooks/common"); + _mkdir("$GL_ADMIN_BASE/hooks/gitolite-admin"); + _print( "$GL_ADMIN_BASE/hooks/common/update", update_hook() ); + _print( "$GL_ADMIN_BASE/hooks/gitolite-admin/post-update", post_update_hook() ); + chmod 0755, "$GL_ADMIN_BASE/hooks/common/update"; + chmod 0755, "$GL_ADMIN_BASE/hooks/gitolite-admin/post-update"; + $hook_reset++; + } + + # propagate user hooks + ln_sf( "$GL_ADMIN_BASE/hooks/common", "*", "$repo.git/hooks" ); + + # propagate admin hook + ln_sf( "$GL_ADMIN_BASE/hooks/gitolite-admin", "*", "$repo.git/hooks" ) if $repo eq 'gitolite-admin'; + + # g2 diff: no "site-wide" hooks (the stuff in between gitolite hooks + # and user hooks) anymore. I don't think anyone used them anyway... + } +} + +sub inside_out { + my $href = shift; + # input conf: @aa = bb cc @bb = @aa dd + + my %ret = (); + while ( my ( $k, $v ) = each( %{$href} ) ) { + # $k is '@aa', $v is a href + for my $k2 ( keys %{$v} ) { + # $k2 is bb, then cc + push @{ $ret{$k2} }, $k; + } + } + return \%ret; + # %groups = ( 'bb' => [ '@bb', '@aa' ], 'cc' => [ '@bb', '@aa' ], 'dd' => [ '@bb' ]); +} + +1; + diff --git a/Gitolite/Conf/Sugar.pm b/Gitolite/Conf/Sugar.pm new file mode 100644 index 0000000..5db96f2 --- /dev/null +++ b/Gitolite/Conf/Sugar.pm @@ -0,0 +1,82 @@ +package Gitolite::Conf::Sugar; + +# syntactic sugar for the conf file, including site-local macros +# ---------------------------------------------------------------------- + +@EXPORT = qw( + macro_expand + cleanup_conf_line +); + +use Exporter 'import'; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Common; +use Gitolite::Rc; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +sub macro_expand { + # site-local macros, if any, then gitolite internal macros, to munge the + # input conf line if needed + + my @lines = @_; + + # TODO: user macros, how to allow the user to specify them? + + # cheat, to keep *our* regexes simple :) + # XXX but this also kills the special '# BEGIN filename' and '# END + # filename' lines that explode() surrounds the actual data with when it + # called macro_expand(). Right now we don't need it, but... + @lines = grep /\S/, map { cleanup_conf_line($_) } @lines; + + @lines = owner_desc(@lines); + + return @lines; +} + +sub cleanup_conf_line { + my $line = shift; + + # kill comments, but take care of "#" inside *simple* strings + $line =~ s/^((".*?"|[^#"])*)#.*/$1/; + # normalise whitespace; keeps later regexes very simple + $line =~ s/=/ = /; + $line =~ s/\s+/ /g; + $line =~ s/^ //; + $line =~ s/ $//; + return $line; +} + +sub owner_desc { + my @lines = @_; + my @ret; + + for my $line (@lines) { + # reponame = "some description string" + # reponame "owner name" = "some description string" + if ( $line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/ ) { + my ( $repo, $owner, $desc ) = ( $1, $2, $3 ); + # XXX these two checks should go into add_config + # _die "bad repo name '$repo'" unless $repo =~ $REPONAME_PATT; + # _die "$fragment attempting to set description for $repo" + # if check_fragment_repo_disallowed( $fragment, $repo ); + push @ret, "config gitolite-options.repo-desc = $desc"; + push @ret, "config gitolite-options.repo-owner = $owner" if $owner; + } elsif ( $line =~ /^desc = (\S.*)/ ) { + push @ret, "config gitolite-options.repo-desc = $1"; + } elsif ( $line =~ /^owner = (\S.*)/ ) { + my ( $repo, $owner, $desc ) = ( $1, $2, $3 ); + push @ret, "config gitolite-options.repo-owner = $1"; + } else { + push @ret, $line; + } + } + return @ret; +} + +1; + diff --git a/Gitolite/Hooks/PostUpdate.pm b/Gitolite/Hooks/PostUpdate.pm new file mode 100644 index 0000000..813733f --- /dev/null +++ b/Gitolite/Hooks/PostUpdate.pm @@ -0,0 +1,69 @@ +package Gitolite::Hooks::PostUpdate; + +# everything to do with the post-update hook +# ---------------------------------------------------------------------- + +@EXPORT = qw( + post_update + post_update_hook +); + +use Exporter 'import'; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +sub post_update { + trace(3); + # this is the *real* post_update hook for gitolite + + tsh_try("git ls-tree --name-only master"); + _die "no files/dirs called 'hooks' or 'logs' are allowed" if tsh_text() =~ /^(hooks|logs)$/; + + { + local $ENV{GIT_WORK_TREE} = $GL_ADMIN_BASE; + tsh_try("git checkout -f --quiet master"); + } + system("$ENV{GL_BINDIR}/gitolite compile"); + + exit 0; +} + +{ + my $text = ''; + + sub post_update_hook { + trace(1); + if ( not $text ) { + local $/ = undef; + $text = ; + } + return $text; + } +} + +1; + +__DATA__ +#!/usr/bin/perl + +use strict; +use warnings; + +BEGIN { + die "GL_BINDIR not set; aborting\n" unless $ENV{GL_BINDIR}; +} +use lib $ENV{GL_BINDIR}; +use Gitolite::Hooks::PostUpdate; + +# gitolite post-update hook (only for the admin repo) +# ---------------------------------------------------------------------- + +post_update(@ARGV); # is not expected to return +exit 1; # so if it does, something is wrong diff --git a/Gitolite/Hooks/Update.pm b/Gitolite/Hooks/Update.pm new file mode 100644 index 0000000..2c60914 --- /dev/null +++ b/Gitolite/Hooks/Update.pm @@ -0,0 +1,114 @@ +package Gitolite::Hooks::Update; + +# everything to do with the update hook +# ---------------------------------------------------------------------- + +@EXPORT = qw( + update + update_hook +); + +use Exporter 'import'; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Common; +use Gitolite::Conf::Load; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +sub update { + trace( 3, @_ ); + # this is the *real* update hook for gitolite + + my ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ) = args(@ARGV); + + my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref ); + trace( 1, "access($ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref) -> $ret" ); + _die $ret if $ret =~ /DENIED/; + + exit 0; +} + +{ + my $text = ''; + + sub update_hook { + trace(1); + if ( not $text ) { + local $/ = undef; + $text = ; + } + return $text; + } +} + +# ---------------------------------------------------------------------- + +sub args { + my ( $ref, $oldsha, $newsha ) = @_; + my ( $oldtree, $newtree, $aa ); + + # this is special to git -- the hash of an empty tree + my $empty = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'; + $oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha; + $newtree = $newsha eq '0' x 40 ? $empty : $newsha; + + my $merge_base = '0' x 40; + # for branch create or delete, merge_base stays at '0'x40 + chomp( $merge_base = `git merge-base $oldsha $newsha` ) + unless $oldsha eq '0' x 40 + or $newsha eq '0' x 40; + + $aa = 'W'; + # tag rewrite + $aa = '+' if $ref =~ m(refs/tags/) and $oldsha ne ( '0' x 40 ); + # non-ff push to ref (including ref delete) + $aa = '+' if $oldsha ne $merge_base; + + # XXX $aa = 'D' if ( $repos{$ENV{GL_REPO}}{DELETE_IS_D} or $repos{'@all'}{DELETE_IS_D} ) and $newsha eq '0' x 40; + # XXX $aa = 'C' if ( $repos{$ENV{GL_REPO}}{CREATE_IS_C} or $repos{'@all'}{CREATE_IS_C} ) and $oldsha eq '0' x 40; + + # and now "M" commits. This presents a bit of a problem. All the other + # accesses (W, +, C, D) were mutually exclusive in some sense. Sure a W could + # be a C or a + could be a D but that's by design. A merge commit, however, + # could still be any of the others (except a "D"). + + # so we have to *append* 'M' to $aa (if the repo has MERGE_CHECK in + # effect and this push contains a merge inside) + +=for XXX + if ( $repos{ $ENV{GL_REPO} }{MERGE_CHECK} or $repos{'@all'}{MERGE_CHECK} ) { + if ( $oldsha eq '0' x 40 or $newsha eq '0' x 40 ) { + warn "ref create/delete ignored for purposes of merge-check\n"; + } else { + $aa .= 'M' if `git rev-list -n 1 --merges $oldsha..$newsha` =~ /./; + } + } +=cut + + return ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ); +} + +1; + +__DATA__ +#!/usr/bin/perl + +use strict; +use warnings; + +BEGIN { + exit 0 if $ENV{GL_BYPASS_UPDATE_HOOK}; + die "GL_BINDIR not set; aborting\n" unless $ENV{GL_BINDIR}; +} +use lib $ENV{GL_BINDIR}; +use Gitolite::Hooks::Update; + +# gitolite update hook +# ---------------------------------------------------------------------- + +update(@ARGV); # is not expected to return +exit 1; # so if it does, something is wrong diff --git a/Gitolite/Rc.pm b/Gitolite/Rc.pm new file mode 100644 index 0000000..94f6613 --- /dev/null +++ b/Gitolite/Rc.pm @@ -0,0 +1,112 @@ +package Gitolite::Rc; + +# everything to do with 'rc'. Also defines some 'constants' +# ---------------------------------------------------------------------- + +@EXPORT = qw( + $GL_ADMIN_BASE + $GL_REPO_BASE + + $GL_UMASK + + $GL_GITCONFIG_KEYS + + glrc_default_text + glrc_default_filename + glrc_filename + + $ADC_CMD_ARGS_PATT + $REF_OR_FILENAME_PATT + $REPONAME_PATT + $REPOPATT_PATT + $USERNAME_PATT + + $current_data_version +); + +use Exporter 'import'; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Common; + +# variables that are/could be/should be in the rc file +# ---------------------------------------------------------------------- + +$GL_ADMIN_BASE = "$ENV{HOME}/.gitolite"; +$GL_REPO_BASE = "$ENV{HOME}/repositories"; + +# variables that should probably never be changed +# ---------------------------------------------------------------------- + +$current_data_version = "3.0"; + +$ADC_CMD_ARGS_PATT = qr(^[0-9a-zA-Z._\@/+:-]*$); +$REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][0-9a-zA-Z._\@/+ :,-]*$); +$REPONAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$); +$REPOPATT_PATT = qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/,-]*$); +$USERNAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); + +# ---------------------------------------------------------------------- + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +my $rc = glrc_filename(); +do $rc if -r $rc; + +{ + my $glrc_default_text = ''; + + sub glrc_default_text { + trace( 1, "..should happen only on first run" ); + return $glrc_default_text if $glrc_default_text; + local $/ = undef; + $glrc_default_text = ; + } +} + +sub glrc_default_filename { + trace( 1, "..should happen only on first run" ); + return "$ENV{HOME}/.gitolite.rc"; +} + +# where is the rc file? +sub glrc_filename { + trace(4); + + # search $HOME first + return "$ENV{HOME}/.gitolite.rc" if -f "$ENV{HOME}/.gitolite.rc"; + trace( 2, "$ENV{HOME}/.gitolite.rc not found" ); + + # XXX for fedora, we can add the following line, but I would really prefer + # if ~/.gitolite.rc on each $HOME was just a symlink to /etc/gitolite.rc + # XXX return "/etc/gitolite.rc" if -f "/etc/gitolite.rc"; + + return ''; +} + +1; + +# ---------------------------------------------------------------------- + +__DATA__ +# configuration variables for gitolite + +# PLEASE READ THE DOCUMENTATION BEFORE EDITING OR ASKING QUESTIONS + +# this file is in perl syntax. However, you do NOT need to know perl to edit +# it; it should be fairly self-explanatory and easy to maintain + +$GL_UMASK = 0077; +$GL_GITCONFIG_KEYS = ""; + +# ------------------------------------------------------------------------------ +# per perl rules, this should be the last line in such a file: +1; + +# Local variables: +# mode: perl +# End: +# vim: set syn=perl: diff --git a/Gitolite/Test.pm b/Gitolite/Test.pm new file mode 100644 index 0000000..6b4a0ae --- /dev/null +++ b/Gitolite/Test.pm @@ -0,0 +1,34 @@ +package Gitolite::Test; + +# functions for the test code to use +# ---------------------------------------------------------------------- + +#<<< +@EXPORT = qw( + try + put +); +#>>> +use Exporter 'import'; +use File::Path qw(mkpath); +use Carp qw(carp cluck croak confess); + +BEGIN { + require Gitolite::Test::Tsh; + *{'try'} = \&Tsh::try; + *{'put'} = \&Tsh::put; +} + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +# required preamble for all tests +try " + DEF gsh = /TRACE: gsh.SOC=/ + ./g3-install -c admin + cd tsh_tempdir; +"; + +1; diff --git a/Gitolite/Test/Tsh.pm b/Gitolite/Test/Tsh.pm new file mode 100644 index 0000000..b4b3b41 --- /dev/null +++ b/Gitolite/Test/Tsh.pm @@ -0,0 +1,624 @@ +#!/usr/bin/perl +use 5.10.0; + +# Tsh -- non interactive Testing SHell in perl + +# TODO items: +# - allow an RC file to be used to add basic and extended commands +# - convert internal defaults to additions to the RC file +# - implement shell commands as you go +# - solve the "pass/fail" inconsistency between shell and perl +# - solve the pipes problem (use 'overload'?) + +# ---------------------------------------------------------------------- +# modules + +package Tsh; + +use Exporter 'import'; +@EXPORT = qw( + try run AUTOLOAD + rc error_count text lines error_list put + cd tsh_tempdir + + $HOME $PWD $USER +); +@EXPORT_OK = qw(); + +use Env qw(@PATH HOME PWD USER TSH_VERBOSE); +# other candidates: +# GL_ADMINDIR GL_BINDIR GL_RC GL_REPO_BASE_ABS GL_REPO GL_USER + +use strict; +use warnings; + +use Text::Tabs; # only used for formatting the usage() message +use Text::ParseWords; + +use File::Temp qw(tempdir); +END { chdir( $ENV{HOME} ); } +# we need this END handler *after* the 'use File::Temp' above. Without +# this, if $PWD at exit was $tempdir, you get errors like "cannot remove +# path when cwd is [...] at /usr/share/perl5/File/Temp.pm line 902". + +use Data::Dumper; + +# ---------------------------------------------------------------------- +# globals + +my $rc; # return code from backticked (external) programs +my $text; # STDOUT+STDERR of backticked (external) programs +my $lec; # the last external command (the rc and text are from this) +my $cmd; # the current command + +my $testnum; # current test number, for info in TAP output +my $testname; # current test name, for error info to user +my $line; # current line number + +my $err_count; # count of test failures +my @errors_in; # list of testnames that errored + +my $tick; # timestamp for git commits + +my %autoloaded; +my $tempdir = ''; + +# ---------------------------------------------------------------------- +# setup + +# unbuffer STDOUT and STDERR +select(STDERR); $|++; +select(STDOUT); $|++; + +# set the timestamp (needed only under harness) +test_tick() if $ENV{HARNESS_ACTIVE}; + +# ---------------------------------------------------------------------- +# this is for one-liner access from outside, using @ARGV, as in: +# perl -MTsh -e 'tsh()' 'tsh command list' +# or via STDIN +# perl -MTsh -e 'tsh()' < file-containing-tsh-commands +# NOTE: it **exits**! + +sub tsh { + my @lines; + + if (@ARGV) { + # simple, single argument which is a readable filename + if ( @ARGV == 1 and $ARGV[0] !~ /\s/ and -r $ARGV[0] ) { + # take the contents of the file + @lines = <>; + } else { + # more than one argument *or* not readable filename + # just take the arguments themselves as the command list + @lines = @ARGV; + @ARGV = (); + } + } else { + # no arguments given, take STDIN + usage() if -t; + @lines = <>; + } + + # and process them + try(@lines); + + # print error summary by default + if ( not defined $TSH_VERBOSE ) { + say STDERR "$err_count error(s)" if $err_count; + } + + exit $err_count; +} + +# these two get called with series of tsh commands, while the autoload, +# (later) handles single commands + +sub try { + $rc = $err_count = 0; + @errors_in = (); + + # break up multiline arguments into separate lines + my @lines = map { split /\n/ } @_; + + # and process them + rc_lines(@lines); + + # bump err_count if the last command had a non-0 rc (that was apparently not checked). + $err_count++ if $rc; + + # finish up... + dbg( 1, "$err_count error(s)" ) if $err_count; + return ( not $err_count ); +} + +# run() differs from try() in that +# - uses open(), not backticks +# - takes only one command, not tsh-things like ok, /patt/ etc +# - - if you pass it an array it uses the list form! + +sub run { + open( my $fh, "-|", @_ ) or die "tell sitaram $!"; + local $/ = undef; $text = <$fh>; + close $fh; warn "tell sitaram $!" if $!; + $rc = ( $? >> 8 ); + return $text; +} + +sub put { + my ( $file, $data ) = @_; + die "probable quoting error in arguments to put: $file\n" if $file =~ /^\s*['"]/; + my $mode = ">"; + $mode = "|-" if $file =~ s/^\s*\|\s*//; + + $rc = 0; + my $fh; + open( $fh, $mode, $file ) + and print $fh $data + and close $fh + and return 1; + + $rc = 1; + dbg( 1, "put $file: $!" ); + return ''; +} + +# ---------------------------------------------------------------------- +# TODO: AUTOLOAD and exportable convenience subs for common shell commands + +sub cd { + my $dir = shift || ''; + _cd($dir); + dbg( 1, "cd $dir: $!" ) if $rc; + return ( not $rc ); +} + +# this is classic AUTOLOAD, almost from the perlsub manpage. Although, if +# instead of `ls('bin');` you want to be able to say `ls 'bin';` you will need +# to predeclare ls, with `sub ls;`. +sub AUTOLOAD { + my $program = $Tsh::AUTOLOAD; + dbg( 4, "program = $program, arg=$_[0]" ); + $program =~ s/.*:://; + $autoloaded{$program}++; + + die "tsh's autoload support expects only one arg\n" if @_ > 1; + _sh("$program $_[0]"); + return ( not $rc ); # perl truth +} + +# ---------------------------------------------------------------------- +# exportable service subs + +sub rc { + return $rc || 0; +} + +sub text { + return $text || ''; +} + +sub lines { + return split /\n/, $text; +} + +sub error_count { + return $err_count; +} + +sub error_list { + return ( + wantarray + ? @errors_in + : join( "\n", @errors_in ) + ); +} + +sub tsh_tempdir { + # create tempdir if not already done + $tempdir = tempdir( "tsh_tempdir.XXXXXXXXXX", TMPDIR => 1, CLEANUP => 1 ) unless $tempdir; + # XXX TODO that 'UNLINK' doesn't work for Ctrl_C + + return $tempdir; +} + +# ---------------------------------------------------------------------- +# internal (non-exportable) service subs + +sub print_plan { + return unless $ENV{HARNESS_ACTIVE}; + my $_ = shift; + say "1..$_"; +} + +sub rc_lines { + my @lines = @_; + + while (@lines) { + my $_ = shift @lines; + chomp; $_ = trim_ws($_); + + # this also sets $testname + next if is_comment_or_empty($_); + + dbg( 2, "L: $_" ); + $line = $_; # save line for printing with 'FAIL:' + + # a DEF has to be on a line by itself + if (/^DEF\s+([-.\w]+)\s*=\s*(\S.*)$/) { + def( $1, $2 ); + next; + } + + my @cmds = cmds($_); + + # process each command + # (note: some of the commands may put stuff back into @lines) + while (@cmds) { + # this needs to be the 'global' one, since fail() prints it + $cmd = shift @cmds; + + # is the current command a "testing" command? + my $testing_cmd = + ( $cmd =~ m(^ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^!ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^/(.*?)/(?:\s+or\s+(.*))?$) or $cmd =~ m(^!/(.*?)/(?:\s+or\s+(.*))?$) ); + + # warn if the previous command failed but rc is not being checked + if ( $rc and not $testing_cmd ) { + dbg( 1, "rc: $rc from cmd prior to '$cmd'\n" ); + # count this as a failure, for exit status purposes + $err_count++; + # and reset the rc, otherwise for example 'ls foo; tt; tt; tt' + # will tell you there are 3 errors! + $rc = 0; + push @errors_in, $testname if $testname; + } + + # prepare to run the command + dbg( 3, "C: $cmd" ); + if ( def($cmd) ) { + # expand macro and replace head of @cmds (unshift) + dbg( 2, "DEF: $cmd" ); + unshift @cmds, cmds( def($cmd) ); + } else { + parse($cmd); + } + # reset rc if checking is done + $rc = 0 if $testing_cmd; + # assumes you will (a) never have *both* 'ok' and '!ok' after + # an action command, and (b) one of them will come immediately + # after the action command, with /patt/ only after it. + } + } +} + +sub def { + my ( $cmd, $list ) = @_; + state %def; + %def = read_rc_file() unless %def; + + if ($list) { + # set mode + die "attempt to redefine macro $cmd\n" if $def{$cmd}; + $def{$cmd} = $list; + return; + } + + # get mode: split the $cmd at spaces, see if there is a definition + # available, substitute any %1, %2, etc., in it and send it back + my ( $c, @d ) = shellwords($cmd); + my $e; # the expanded value + if ( $e = $def{$c} ) { # starting value + for my $i ( 1 .. 9 ) { + last unless $e =~ /%$i/; # no more %N's (we assume sanity) + die "$def{$c} requires more arguments\n" unless @d; + my $f = shift @d; # get the next datum + $e =~ s/%$i/$f/g; # and substitute %N all over + } + return join( " ", $e, @d ); # join up any remaining data + } + return ''; +} + +sub _cd { + my $dir = shift || $HOME; + # a directory name of 'tsh_tempdir' is special + $dir = tsh_tempdir() if $dir eq 'tsh_tempdir'; + $rc = 0; + chdir($dir) or $rc = 1; +} + +sub _sh { + my $cmd = shift; + # TODO: switch to IPC::Open3 or something...? + + dbg( 4, " running: ( $cmd ) 2>&1" ); + $text = `( $cmd ) 2>&1; echo -n RC=\$?`; + $lec = $cmd; + dbg( 4, " results:\n$text" ); + + if ( $text =~ /RC=(\d+)$/ ) { + $rc = $1; + $text =~ s/RC=\d+$//; + } else { + die "couldnt find RC= in result; this should not happen:\n$text\n\n...\n"; + } +} + +sub _perl { + my $perl = shift; + local $_; + $_ = $text; + + dbg( 4, " eval: $perl" ); + my $evrc = eval $perl; + + if ($@) { + $rc = 1; # shell truth + dbg( 1, $@ ); + # leave $text unchanged + } else { + $rc = not $evrc; + # $rc is always shell truth, so we need to cover the case where + # there was no error but it still returned a perl false + $text = $_; + } + dbg( 4, " eval-rc=$evrc, results:\n$text" ); +} + +sub parse { + my $cmd = shift; + + if ( $cmd =~ /^sh (.*)/ ) { + + _sh($1); + + } elsif ( $cmd =~ /^perl (.*)/ ) { + + _perl($1); + + } elsif ( $cmd eq 'tt' or $cmd eq 'test-tick' ) { + + test_tick(); + + } elsif ( $cmd =~ /^plan ?(\d+)$/ ) { + + print_plan($1); + + } elsif ( $cmd =~ /^cd ?(\S*)$/ ) { + + _cd($1); + + } elsif ( $cmd =~ /^ENV (\w+)=['"]?(.+?)['"]?$/ ) { + + $ENV{$1} = $2; + + } elsif ( $cmd =~ /^(?:tc|test-commit)\s+(\S.*)$/ ) { + + # this is the only "git special" really; the default expansions are + # just that -- defaults. But this one is hardwired! + dummy_commits($1); + + } elsif ( $cmd =~ '^put(?:\s+(\S.*))?$' ) { + + if ($1) { + put( $1, $text ); + } else { + print $text if defined $text; + } + + } elsif ( $cmd =~ m(^ok(?:\s+or\s+(.*))?$) ) { + + $rc ? fail( "ok, rc=$rc from $lec", $1 || '' ) : ok(); + + } elsif ( $cmd =~ m(^!ok(?:\s+or\s+(.*))?$) ) { + + $rc ? ok() : fail( "!ok, rc=0 from $lec", $1 || '' ); + + } elsif ( $cmd =~ m(^/(.*?)/(?:\s+or\s+(.*))?$) ) { + + expect( $1, $2 ); + + } elsif ( $cmd =~ m(^!/(.*?)/(?:\s+or\s+(.*))?$) ) { + + not_expect( $1, $2 ); + + } else { + + _sh($cmd); + + } +} + +# currently unused +sub executable { + my $cmd = shift; + # path supplied + $cmd =~ m(/) and -x $cmd and return 1; + # barename; look up in $PATH + for my $p (@PATH) { + -x "$p/$cmd" and return 1; + } + return 0; +} + +sub ok { + $testnum++; + say "ok ($testnum)" if $ENV{HARNESS_ACTIVE}; +} + +sub fail { + $testnum++; + say "not ok ($testnum)" if $ENV{HARNESS_ACTIVE}; + + my $die = 0; + my ( $msg1, $msg2 ) = @_; + if ($msg2) { + # if arg2 is non-empty, print it regardless of debug level + $die = 1 if $msg2 =~ s/^die //; + say STDERR "# $msg2"; + } + + local $TSH_VERBOSE = 1 if $ENV{TSH_ERREXIT}; + dbg( 1, "FAIL: $msg1", $testname || '', "test number $testnum", "L: $line", "results:\n$text" ); + + # count the error and add the testname to the list if it is set + $err_count++; + push @errors_in, $testname if $testname; + + return unless $die or $ENV{TSH_ERREXIT}; + dbg( 1, "exiting at cmd $cmd\n" ); + + exit( $rc || 74 ); +} + +sub expect { + my ( $patt, $msg ) = @_; + $msg =~ s/^\s+// if $msg; + my $sm; + if ( $sm = sm($patt) ) { + dbg( 4, " M: $sm" ); + ok(); + } else { + fail( "/$patt/", $msg || '' ); + } +} + +sub not_expect { + my ( $patt, $msg ) = @_; + $msg =~ s/^\s+// if $msg; + my $sm; + if ( $sm = sm($patt) ) { + dbg( 4, " M: $sm" ); + fail( "!/$patt/", $msg || '' ); + } else { + ok(); + } +} + +sub sm { + # smart match? for now we just do regex match + my $patt = shift; + + return ( $text =~ qr($patt) ? $& : "" ); +} + +sub trim_ws { + my $_ = shift; + s/^\s+//; s/\s+$//; + return $_; +} + +sub is_comment_or_empty { + my $_ = shift; + chomp; $_ = trim_ws($_); + if (/^##\s(.*)/) { + $testname = $1; + say "# $1"; + } + return ( /^#/ or /^$/ ); +} + +sub cmds { + my $_ = shift; + chomp; $_ = trim_ws($_); + + # split on unescaped ';'s, then unescape the ';' in the results + my @cmds = map { s/\\;/;/g; $_ } split /(?= $level; + my $all = join( "\n", grep( /./, @_ ) ); + chomp($all); + $all =~ s/\n/\n\t/g; + say STDERR "# $all"; +} + +sub ddump { + for my $i (@_) { + print STDERR "DBG: " . Dumper($i); + } +} + +sub usage { + # TODO + print "Please see documentation at: + + https://github.com/sitaramc/tsh/blob/master/README.mkd + +Meanwhile, here are your local 'macro' definitions: + +"; + my %m = read_rc_file(); + my @m = map { "$_\t$m{$_}\n" } sort keys %m; + $tabstop = 16; + print join( "", expand(@m) ); + exit 1; +} + +# ---------------------------------------------------------------------- +# git-specific internal service subs + +sub dummy_commits { + for my $f ( split ' ', shift ) { + if ( $f eq 'tt' or $f eq 'test-tick' ) { + test_tick(); + next; + } + my $ts = ( $tick ? localtime($tick) : localtime() ); + _sh("echo $f at $ts >> $f && git add $f && git commit -m '$f at $ts'"); + } +} + +sub test_tick { + unless ( $ENV{HARNESS_ACTIVE} ) { + sleep 1; + return; + } + $tick += 60 if $tick; + $tick ||= 1310000000; + $ENV{GIT_COMMITTER_DATE} = "$tick +0530"; + $ENV{GIT_AUTHOR_DATE} = "$tick +0530"; +} + +# ---------------------------------------------------------------------- +# the internal macros, for easy reference and reading + +sub read_rc_file { + my $rcfile = "$HOME/.tshrc"; + my $rctext; + if ( -r $rcfile ) { + local $/ = undef; + open( my $rcfh, "<", $rcfile ) or die "this should not happen: $!\n"; + $rctext = <$rcfh>; + } else { + # this is the default "rc" content + $rctext = " + add = git add + branch = git branch + clone = git clone + checkout = git checkout + commit = git commit + fetch = git fetch + init = git init + push = git push + reset = git reset + tag = git tag + + empty = git commit --allow-empty -m empty + push-om = git push origin master + reset-h = git reset --hard + reset-hu = git reset --hard \@{u} + " + } + + # ignore everything except lines of the form "aa = bb cc dd" + my %commands = ( $rctext =~ /^\s*([-.\w]+)\s*=\s*(\S.*)$/gm ); + return %commands; +} + +1; diff --git a/g3-info b/g3-info new file mode 100755 index 0000000..d4db40e --- /dev/null +++ b/g3-info @@ -0,0 +1,35 @@ +#!/usr/bin/perl + +# gitolite shell, invoked from ~/.ssh/authorized_keys +# ---------------------------------------------------------------------- + +BEGIN { + # find and set bin dir + $ENV{GL_BINDIR} = "$ENV{HOME}/bin"; +} + +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; +use Gitolite::Conf::Load; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +my $user = shift or die; +my $aa; +my $ref = 'unknown'; + +my $ret; +while (<>) { + chomp; + + my $perm = ''; + for $aa (qw(R W C)) { + $ret = access($_, $user, $aa, $ref); + $perm .= ( $ret =~ /DENIED/ ? " " : " $aa" ); + } + print "$perm\t$_\n" if $perm =~ /\S/; +} diff --git a/g3-install b/g3-install new file mode 100755 index 0000000..ef40012 --- /dev/null +++ b/g3-install @@ -0,0 +1,20 @@ +#!/bin/bash + +# this is specific to my test env; you may want to change it + +set -e + +cd /home/g3 + +if [ "$1" = "-c" ] +then + rm -rf .gito* gito* repositories proj* bin + mkdir bin + cp ~/.ssh/id_rsa.pub ~/.ssh/admin.pub + + cd g3; cp -a gito* Gito* t/glt t/gito* ~/bin + gitolite setup -a ${2:-admin} -pk ~/.ssh/admin.pub +else + cd g3; cp -a gito* Gito* t/glt t/gito* ~/bin + gitolite setup +fi diff --git a/gitolite b/gitolite new file mode 100755 index 0000000..f89f12f --- /dev/null +++ b/gitolite @@ -0,0 +1,54 @@ +#!/usr/bin/perl + +# all gitolite CLI tools run as sub-commands of this command +# ---------------------------------------------------------------------- + +=for usage +Usage: gitolite [sub-command] [options] + +The following subcommands are available; they should all respond to '-h': + + setup 1st run: initial setup; all runs: hook fixups + compile compile gitolite.conf + query-rc get values of rc variables +=cut + +# ---------------------------------------------------------------------- + +use FindBin; + +BEGIN { $ENV{GL_BINDIR} = $FindBin::Bin; } +use lib $ENV{GL_BINDIR}; +use Gitolite::Common; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +args(); + +# ---------------------------------------------------------------------- + +sub args { + my ( $command, @args ) = @ARGV; + usage() if not $command or $command eq '-h'; + + if ( $command eq 'setup' ) { + shift @ARGV; + require Gitolite::Commands::Setup; + Gitolite::Commands::Setup->import; + setup(); + } elsif ( $command eq 'compile' ) { + shift @ARGV; + _die "'gitolite compile' does not take any arguments" if @ARGV; + require Gitolite::Conf; + Gitolite::Conf->import; + compile(); + } elsif ( $command eq 'query-rc' ) { + shift @ARGV; + require Gitolite::Commands::QueryRc; + Gitolite::Commands::QueryRc->import; + query_rc(); + } +} diff --git a/gitolite-shell b/gitolite-shell new file mode 100755 index 0000000..479773c --- /dev/null +++ b/gitolite-shell @@ -0,0 +1,57 @@ +#!/usr/bin/perl + +# gitolite shell, invoked from ~/.ssh/authorized_keys +# ---------------------------------------------------------------------- + +BEGIN { + # find and set bin dir + $0 =~ m|^(/)?(.*)/| and $ENV{GL_BINDIR} = ( $1 || "$ENV{PWD}/" ) . $2; +} + +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; +use Gitolite::Conf::Load; + +use strict; +use warnings; +print STDERR "TRACE: gsh(", join( ")(", @ARGV ), ")\n"; +print STDERR "TRACE: gsh(SOC=$ENV{SSH_ORIGINAL_COMMAND})\n"; + +# ---------------------------------------------------------------------- + +# XXX lots of stuff from gl-auth-command is missing for now... + +# set up the user +my $user = $ENV{GL_USER} = shift; + +# set up the repo and the attempted access +my ( $verb, $repo ) = split_soc(); +sanity($repo); +$ENV{GL_REPO} = $repo; +my $aa = ( $verb =~ 'upload' ? 'R' : 'W' ); + +# a ref of 'unknown' signifies that this is a pre-git check, where we don't +# yet know the ref that will be eventually pushed (and even that won't apply +# if it's a read operation). See the matching code in access() for more. +my $ret = access( $repo, $user, $aa, 'unknown' ); +trace( 1, "access($repo, $user, $aa, 'unknown') -> $ret" ); +_die $ret if $ret =~ /DENIED/; + +$repo = "'$GL_REPO_BASE/$repo.git'"; +exec( "git", "shell", "-c", "$verb $repo" ); + +# ---------------------------------------------------------------------- + +sub split_soc { + my $soc = $ENV{SSH_ORIGINAL_COMMAND}; + return ( $1, $2 ) if $soc =~ m(^(git-(?:upload|receive)-pack) '/?(.*?)(?:\.git)?'$); + _die "unknown command: $soc"; +} + +sub sanity { + my $repo = shift; + _die "'$repo' contains bad characters" if $repo !~ $REPONAME_PATT; + _die "'$repo' ends with a '/'" if $repo =~ m(/$); + _die "'$repo' contains '..'" if $repo =~ m(\.\.$); +} diff --git a/src/gitolite b/src/gitolite new file mode 100755 index 0000000..c6a1f54 --- /dev/null +++ b/src/gitolite @@ -0,0 +1,106 @@ +#!/usr/bin/perl + +# all gitolite CLI tools run as sub-commands of this command +# ---------------------------------------------------------------------- + +=for args +Usage: gitolite [] [] + +The following built-in subcommands are available; they should all respond to +'-h' if you want further details on each: + + setup 1st run: initial setup; all runs: hook fixups + compile compile gitolite.conf + + query-rc get values of rc variables + + list-groups list all group names in conf + list-users list all users/user groups in conf + list-repos list all repos/repo groups in conf + list-phy-repos list all repos actually on disk + list-memberships list all groups a name is a member of + list-members list all members of a group + +Warnings: + - list-users is disk bound and could take a while on sites with 1000s of repos + - list-memberships does not check if the name is known; unknown names come + back with 2 answers: the name itself and '@all' + +In addition, running 'gitolite help' should give you a list of custom commands +available. They may or may not respond to '-h', depending on how they were +written. +=cut + +# ---------------------------------------------------------------------- + +use FindBin; + +BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin; } +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +my ( $command, @args ) = @ARGV; +gl_log( 'gitolite', @ARGV ) if -d $rc{GL_ADMIN_BASE}; +args(); + +# the first two commands need options via @ARGV, as they have their own +# GetOptions calls and older perls don't have 'GetOptionsFromArray' + +if ( $command eq 'setup' ) { + shift @ARGV; + require Gitolite::Setup; + Gitolite::Setup->import; + setup(); + +} elsif ( $command eq 'query-rc' ) { + shift @ARGV; + query_rc(); + +# the rest don't need @ARGV per se + +} elsif ( $command eq 'compile' ) { + require Gitolite::Conf; + Gitolite::Conf->import; + compile(@args); + +} elsif ( $command eq 'trigger' ) { + trigger(@args); + +} elsif ( -x "$rc{GL_BINDIR}/commands/$command" ) { + trace( 2, "attempting gitolite command $command" ); + run_command( $command, @args ); + +} elsif ( $command eq 'list-phy-repos' ) { + _chdir( $rc{GL_REPO_BASE} ); + print "$_\n" for ( @{ list_phy_repos(@args) } ); + +} elsif ( $command =~ /^list-/ ) { + trace( 2, "attempting lister command $command" ); + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + my $fn = lister_dispatch($command); + print "$_\n" for ( @{ $fn->(@args) } ); + +} else { + _die "unknown gitolite sub-command"; +} + +sub args { + usage() if not $command or $command eq '-h'; +} + +# ---------------------------------------------------------------------- + +sub run_command { + my $pgm = shift; + my $fullpath = "$ENV{GL_BINDIR}/commands/$pgm"; + _die "$pgm not found or not executable" if not -x $fullpath; + _system( $fullpath, @_ ); + exit 0; +} diff --git a/t/gitolite-receive-pack b/t/gitolite-receive-pack new file mode 100755 index 0000000..48c7428 --- /dev/null +++ b/t/gitolite-receive-pack @@ -0,0 +1,12 @@ +#!/usr/bin/perl + +use strict; +use warnings; +print STDERR "TRACE: grp(", join( ")(", @ARGV ), ")\n"; + +my $repo = shift; +$repo =~ s/\.git$//; +my $user = $ENV{G3T_USER} || 'no-such-user'; + +$ENV{SSH_ORIGINAL_COMMAND} = "git-receive-pack '$repo'"; +exec( "$ENV{HOME}/bin/gitolite-shell", $user ); diff --git a/t/gitolite-upload-pack b/t/gitolite-upload-pack new file mode 100755 index 0000000..8888abb --- /dev/null +++ b/t/gitolite-upload-pack @@ -0,0 +1,12 @@ +#!/usr/bin/perl + +use strict; +use warnings; +print STDERR "TRACE: gup(", join( ")(", @ARGV ), ")\n"; + +my $repo = shift; +$repo =~ s/\.git$//; +my $user = $ENV{G3T_USER} || 'no-such-user'; + +$ENV{SSH_ORIGINAL_COMMAND} = "git-upload-pack '$repo'"; +exec( "$ENV{HOME}/bin/gitolite-shell", $user ); diff --git a/t/glt b/t/glt new file mode 100755 index 0000000..b5704f5 --- /dev/null +++ b/t/glt @@ -0,0 +1,28 @@ +#!/usr/bin/perl +use strict; +use warnings; + +print STDERR "TRACE: glt(", join( ")(", @ARGV ), ")\n"; + +my $cmd = shift or die "need command"; +my $user = shift or die "need user"; +my $rc; + +$ENV{G3T_USER} = $user; +if ( $cmd eq 'push' ) { + $rc = system( "git", $cmd, "--receive-pack=$ENV{HOME}/bin/gitolite-receive-pack", @ARGV ); +} else { + $rc = system( "git", $cmd, "--upload-pack=$ENV{HOME}/bin/gitolite-upload-pack", @ARGV ); +} + +if ( $? == -1 ) { + die "F: failed to execute: $!\n"; +} elsif ( $? & 127 ) { + printf STDERR "E: child died with signal %d\n", ( $? & 127 ); + exit 1; +} else { + printf STDERR "W: child exited with value %d\n", $? >> 8 if $? >> 8; + exit( $? >> 8 ); +} + +exit 0; diff --git a/t/t01-basic b/t/t01-basic new file mode 100755 index 0000000..3970308 --- /dev/null +++ b/t/t01-basic @@ -0,0 +1,110 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "$ENV{HOME}/bin"; +use Gitolite::Test; + +# basic tests +# ---------------------------------------------------------------------- + +try " + plan 74 + + ## clone + glt clone dev2 file://gitolite-admin + !ok; gsh + /FATAL: DENIED: R access to gitolite-admin by dev2 .fallthru./ + /fatal: The remote end hung up unexpectedly/ + glt clone admin --progress file://gitolite-admin + ok; gsh + /Counting/; /Compressing/; /Total/ + cd gitolite-admin; ok + "; + +put "conf/gitolite.conf", " + \@admins = admin dev1 + repo gitolite-admin + - mm = \@admins + RW = \@admins + RW+ = admin + + repo testing + RW+ = \@all +"; + +try " + ## push + git add conf; ok + git status -s; ok; /M conf/gitolite.conf/ + git commit -m t01a; ok; /master.*t01a/ + glt push dev2 origin; !ok; gsh + /FATAL: DENIED: W access to gitolite-admin by dev2 .fallthru./ + /fatal: The remote end hung up unexpectedly/ + glt push admin origin; ok; /master -. master/ + tsh empty; ok; + glt push admin origin master:mm + !ok; gsh + /FATAL: DENIED: W access to gitolite-admin by admin .rule: refs/heads/mm./ + /remote: error: hook declined to update refs/heads/mm/ + /To file://gitolite-admin/ + /remote rejected. master -. mm .hook declined./ + /error: failed to push some refs to 'file://gitolite-admin'/ + + "; + +put "conf/gitolite.conf", " + \@admins = admin dev1 + repo gitolite-admin + RW+ = admin + + repo testing + RW+ = \@all + + repo t1 + R = u2 + RW = u3 + RW+ = u4 +"; + +try " + ## push 2 + git add conf; ok + git status -s; ok; /M conf/gitolite.conf/ + git commit -m t01b; ok; /master.*t01b/ + glt push admin origin; ok; gsh + /master -. master/ + + ## clone + cd ..; ok; + glt clone u1 file://t1; !ok; gsh + /FATAL: DENIED: R access to t1 by u1 .fallthru./ + /fatal: The remote end hung up unexpectedly/ + glt clone u2 file://t1; ok; gsh + /warning: You appear to have cloned an empty repository./ + ls -al t1; ok; /$ENV{USER}.*$ENV{USER}.*\.git/ + cd t1; ok; + + ## push + test-commit tc1 tc2 tc2; ok; /f7153e3/ + glt push u2 origin; !ok; gsh + /FATAL: DENIED: W access to t1 by u2 .fallthru./ + /fatal: The remote end hung up unexpectedly/ + glt push u3 origin master; ok; gsh + /master -. master/ + + ## rewind + reset-h HEAD^; ok; /HEAD is now at 537f964 tc2/ + test-tick; test-commit tc3; ok; /a691552/ + glt push u3 origin; !ok; gsh + /rejected.*master -. master.*non-fast-forward./ + glt push u3 -f origin; !ok; gsh + /FATAL: DENIED: \\+ access to t1 by u3 .fallthru./ + /remote: error: hook declined to update refs/heads/master/ + /To file://t1/ + /remote rejected. master -. master .hook declined./ + /error: failed to push some refs to 'file://t1'/ + glt push u4 origin +master; ok; gsh + / \\+ f7153e3...a691552 master -. master.*forced update./ +" From b0ceac2594c17b06c1175845757b7a9bba5d1d9a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 8 Mar 2012 12:31:01 +0530 Subject: [PATCH 502/850] chdirs moved from load() to load_common() and load_1() --- Gitolite/Conf/Load.pm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Gitolite/Conf/Load.pm b/Gitolite/Conf/Load.pm index db11679..af62812 100644 --- a/Gitolite/Conf/Load.pm +++ b/Gitolite/Conf/Load.pm @@ -43,8 +43,8 @@ my $last_repo = ''; trace( 4, "$repo" ); if ( $repo ne $loaded_repo ) { trace( 3, "loading $repo..." ); - _chdir("$GL_ADMIN_BASE"); load_common(); - _chdir("$GL_REPO_BASE"); load_1($repo); + load_common(); + load_1($repo); $loaded_repo = $repo; } } @@ -86,6 +86,8 @@ sub access { sub load_common { + _chdir("$GL_ADMIN_BASE"); + # we take an unusual approach to caching this function! # (requires that first call to load_common is before first call to load_1) if ( $last_repo and $split_conf{$last_repo} ) { @@ -110,6 +112,8 @@ sub load_1 { my $repo = shift; trace( 4, $repo ); + _chdir("$GL_REPO_BASE"); + if ( $repo eq $last_repo ) { $repos{$repo} = $one_repo{$repo}; $configs{$repo} = $one_config{$repo} if $one_config{$repo}; From 1be66dc10e0fe2130fbc8cfb6ddd19c1c20b84e5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 8 Mar 2012 12:13:50 +0530 Subject: [PATCH 503/850] 'gitolite list-groups' added --- Gitolite/Conf/Load.pm | 19 +++++++++++++++++++ gitolite | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/Gitolite/Conf/Load.pm b/Gitolite/Conf/Load.pm index af62812..34b6a1e 100644 --- a/Gitolite/Conf/Load.pm +++ b/Gitolite/Conf/Load.pm @@ -6,6 +6,8 @@ package Gitolite::Conf::Load; @EXPORT = qw( load access + + list_groups ); use Exporter 'import'; @@ -169,5 +171,22 @@ sub data_version_mismatch { return $data_version ne $current_data_version; } +# ---------------------------------------------------------------------- +# api functions +# ---------------------------------------------------------------------- + +# list all groups +sub list_groups { + die "\nUsage: gitolite list-groups\n\n(no options, no flags)\n\n" if @ARGV; + + load_common(); + + my @g = (); + while (my ($k, $v) = each ( %groups )) { + push @g, @{ $v }; + } + return (sort_u(\@g)); +} + 1; diff --git a/gitolite b/gitolite index f89f12f..43868a5 100755 --- a/gitolite +++ b/gitolite @@ -11,6 +11,7 @@ The following subcommands are available; they should all respond to '-h': setup 1st run: initial setup; all runs: hook fixups compile compile gitolite.conf query-rc get values of rc variables + list-groups list all group names in conf =cut # ---------------------------------------------------------------------- @@ -50,5 +51,10 @@ sub args { require Gitolite::Commands::QueryRc; Gitolite::Commands::QueryRc->import; query_rc(); + } elsif ( $command eq 'list-groups' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_groups() } ); } } From d88cdbefd644185a332f5854f027780cb8ceca30 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 8 Mar 2012 15:06:05 +0530 Subject: [PATCH 504/850] 'gitolite list-users' added (but see warnings) this is pretty slow if you have thousands of repos, since it has to read and parse a 'gl-conf' file for every repo. (For example, on a Lenovo X201 thinkpad with 11170 repos and a cold cache, it took 288 seconds). (With a hot cache -- like if you run the command again -- it took 2.1 seconds! So if you have a fast disk this may not be an issue for you even if you have 10,000+ repos). --- Gitolite/Conf/Load.pm | 38 ++++++++++++++++++++++++++++++++++++++ gitolite | 9 +++++++++ 2 files changed, 47 insertions(+) diff --git a/Gitolite/Conf/Load.pm b/Gitolite/Conf/Load.pm index 34b6a1e..c9b09b5 100644 --- a/Gitolite/Conf/Load.pm +++ b/Gitolite/Conf/Load.pm @@ -8,6 +8,7 @@ package Gitolite::Conf::Load; access list_groups + list_users ); use Exporter 'import'; @@ -188,5 +189,42 @@ sub list_groups { return (sort_u(\@g)); } +sub list_users { + my $count = 0; + my $total = 0; + + die "\nUsage: gitolite list-users\n\n - no options, no flags\n - may be slow if you have thousands of repos\n\n" if @ARGV; + + load_common(); + + my @u = map { keys %{ $_ } } values %repos; + $total = scalar(keys %split_conf); + warn "WARNING: you have $total repos to check; this could take some time!\n" if $total > 100; + for my $one ( keys %split_conf ) { + load_1($one); + $count++; print STDERR "$count / $total\r" if not ( $count % 100 ) and timer(5); + push @u, map { keys %{ $_ } } values %one_repo; + } + print STDERR "\n"; + return (sort_u(\@u)); +} + +# ---------------------------------------------------------------------- + +{ + my $start_time = 0; + + sub timer { + unless ($start_time) { + $start_time = time(); + return 0; + } + my $elapsed = shift; + return 0 if time() - $start_time < $elapsed; + $start_time = time(); + return 1; + } +} + 1; diff --git a/gitolite b/gitolite index 43868a5..fb835e2 100755 --- a/gitolite +++ b/gitolite @@ -12,6 +12,10 @@ The following subcommands are available; they should all respond to '-h': compile compile gitolite.conf query-rc get values of rc variables list-groups list all group names in conf + list-users list all user names in conf + +Warnings: + - list-users is disk bound and could take a while on sites with thousands of repos =cut # ---------------------------------------------------------------------- @@ -56,5 +60,10 @@ sub args { require Gitolite::Conf::Load; Gitolite::Conf::Load->import; print "$_\n" for ( @{ list_groups() } ); + } elsif ( $command eq 'list-users' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_users() } ); } } From 4e81d3cfeddb3c30a8c306377d514b2b3375eee0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 8 Mar 2012 16:14:13 +0530 Subject: [PATCH 505/850] 'gitolite list-repos' added, plus some usage message changes --- Gitolite/Conf/Load.pm | 37 +++++++++++++++++++++++++++++++++++-- gitolite | 8 +++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Gitolite/Conf/Load.pm b/Gitolite/Conf/Load.pm index c9b09b5..130850a 100644 --- a/Gitolite/Conf/Load.pm +++ b/Gitolite/Conf/Load.pm @@ -9,6 +9,7 @@ package Gitolite::Conf::Load; list_groups list_users + list_repos ); use Exporter 'import'; @@ -178,7 +179,13 @@ sub data_version_mismatch { # list all groups sub list_groups { - die "\nUsage: gitolite list-groups\n\n(no options, no flags)\n\n" if @ARGV; + die " +Usage: gitolite list-groups + + - lists all group names in conf + - no options, no flags + +" if @ARGV; load_common(); @@ -193,7 +200,14 @@ sub list_users { my $count = 0; my $total = 0; - die "\nUsage: gitolite list-users\n\n - no options, no flags\n - may be slow if you have thousands of repos\n\n" if @ARGV; + die " +Usage: gitolite list-users + + - lists all users/user groups in conf + - no options, no flags + - WARNING: may be slow if you have thousands of repos + +" if @ARGV; load_common(); @@ -209,6 +223,25 @@ sub list_users { return (sort_u(\@u)); } + +sub list_repos { + + die " +Usage: gitolite list-repos + + - lists all repos/repo groups in conf + - no options, no flags + +" if @ARGV; + + load_common(); + + my @r = keys %repos; + push @r, keys %split_conf; + + return (sort_u(\@r)); +} + # ---------------------------------------------------------------------- { diff --git a/gitolite b/gitolite index fb835e2..6adc347 100755 --- a/gitolite +++ b/gitolite @@ -12,7 +12,8 @@ The following subcommands are available; they should all respond to '-h': compile compile gitolite.conf query-rc get values of rc variables list-groups list all group names in conf - list-users list all user names in conf + list-users list all users/user groups in conf + list-repos list all repos/repo groups in conf Warnings: - list-users is disk bound and could take a while on sites with thousands of repos @@ -65,5 +66,10 @@ sub args { require Gitolite::Conf::Load; Gitolite::Conf::Load->import; print "$_\n" for ( @{ list_users() } ); + } elsif ( $command eq 'list-repos' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_repos() } ); } } From c9826eee077a0c43977398efeed6fc2855e31d69 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 8 Mar 2012 17:00:27 +0530 Subject: [PATCH 506/850] 'gitolite list-memberships' added --- Gitolite/Conf/Load.pm | 18 ++++++++++++++++++ gitolite | 10 +++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Gitolite/Conf/Load.pm b/Gitolite/Conf/Load.pm index 130850a..cc88225 100644 --- a/Gitolite/Conf/Load.pm +++ b/Gitolite/Conf/Load.pm @@ -10,6 +10,7 @@ package Gitolite::Conf::Load; list_groups list_users list_repos + list_memberships ); use Exporter 'import'; @@ -242,6 +243,23 @@ Usage: gitolite list-repos return (sort_u(\@r)); } +sub list_memberships { + + die " +Usage: gitolite list-memberships + + - list all groups a name is a member of + - takes one user/repo name + +" if @ARGV and $ARGV[0] eq '-h' or not @ARGV and not @_; + + my $name = ( @_ ? shift @_ : shift @ARGV ); + + load_common(); + my @m = memberships($name); + return (sort_u(\@m)); +} + # ---------------------------------------------------------------------- { diff --git a/gitolite b/gitolite index 6adc347..5d5f3dd 100755 --- a/gitolite +++ b/gitolite @@ -14,9 +14,12 @@ The following subcommands are available; they should all respond to '-h': list-groups list all group names in conf list-users list all users/user groups in conf list-repos list all repos/repo groups in conf + list-memberships list all groups a name is a member of Warnings: - - list-users is disk bound and could take a while on sites with thousands of repos + - list-users is disk bound and could take a while on sites with 1000s of repos + - list-memberships does not check if the name is known; unknown names come + back with 2 answers: the name itself and '@all' =cut # ---------------------------------------------------------------------- @@ -71,5 +74,10 @@ sub args { require Gitolite::Conf::Load; Gitolite::Conf::Load->import; print "$_\n" for ( @{ list_repos() } ); + } elsif ( $command eq 'list-memberships' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_memberships() } ); } } From 00934c83045262a1c38214ca94b8bb55af3a1426 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 8 Mar 2012 17:59:44 +0530 Subject: [PATCH 507/850] 'gitolite list-members' added --- Gitolite/Conf/Load.pm | 25 +++++++++++++++++++++++++ gitolite | 6 ++++++ 2 files changed, 31 insertions(+) diff --git a/Gitolite/Conf/Load.pm b/Gitolite/Conf/Load.pm index cc88225..9367296 100644 --- a/Gitolite/Conf/Load.pm +++ b/Gitolite/Conf/Load.pm @@ -11,6 +11,7 @@ package Gitolite::Conf::Load; list_users list_repos list_memberships + list_members ); use Exporter 'import'; @@ -260,6 +261,30 @@ Usage: gitolite list-memberships return (sort_u(\@m)); } +sub list_members { + + die " +Usage: gitolite list-members + + - list all members of a group + - takes one group name + +" if @ARGV and $ARGV[0] eq '-h' or not @ARGV and not @_; + + my $name = ( @_ ? shift @_ : shift @ARGV ); + + load_common(); + + my @m = (); + while (my ($k, $v) = each ( %groups )) { + for my $g ( @{ $v } ) { + push @m, $k if $g eq $name; + } + } + + return (sort_u(\@m)); +} + # ---------------------------------------------------------------------- { diff --git a/gitolite b/gitolite index 5d5f3dd..dc7fb0e 100755 --- a/gitolite +++ b/gitolite @@ -15,6 +15,7 @@ The following subcommands are available; they should all respond to '-h': list-users list all users/user groups in conf list-repos list all repos/repo groups in conf list-memberships list all groups a name is a member of + list-members list all members of a group Warnings: - list-users is disk bound and could take a while on sites with 1000s of repos @@ -79,5 +80,10 @@ sub args { require Gitolite::Conf::Load; Gitolite::Conf::Load->import; print "$_\n" for ( @{ list_memberships() } ); + } elsif ( $command eq 'list-members' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_members() } ); } } From 95c6952e11eaaef1285342e1667ba99a07ca873c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 8 Mar 2012 19:20:00 +0530 Subject: [PATCH 508/850] list_phy_repos() moved from store.pm to common.pm but you need to chdir() to the right place before calling it --- Gitolite/Common.pm | 22 +++++++++++++++++++++- Gitolite/Conf/Store.pm | 26 +++----------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Gitolite/Common.pm b/Gitolite/Common.pm index e5d492a..35a5c0f 100644 --- a/Gitolite/Common.pm +++ b/Gitolite/Common.pm @@ -6,7 +6,7 @@ package Gitolite::Common; #<<< @EXPORT = qw( print2 dbg _mkdir _open ln_sf tsh_rc sort_u - say _warn _chdir _print tsh_text + say _warn _chdir _print tsh_text list_phy_repos say2 _die slurp tsh_lines trace tsh_try usage tsh_run @@ -142,6 +142,26 @@ sub sort_u { my @sort_u = sort keys %uniq; return \@sort_u; } + +{ + my @phy_repos = (); + + sub list_phy_repos { + trace(3); + + # use cached value only if it exists *and* no arg was received (i.e., + # receiving *any* arg invalidates cache) + return \@phy_repos if ( @phy_repos and not @_ ); + + for my $repo (`find . -name "*.git" -prune`) { + chomp($repo); + $repo =~ s(\./(.*)\.git$)($1); + push @phy_repos, $repo; + } + return sort_u(\@phy_repos); + } +} + # ---------------------------------------------------------------------- # bare-minimum subset of 'Tsh' (see github.com/sitaramc/tsh) diff --git a/Gitolite/Conf/Store.pm b/Gitolite/Conf/Store.pm index 69056a0..518a750 100644 --- a/Gitolite/Conf/Store.pm +++ b/Gitolite/Conf/Store.pm @@ -195,10 +195,10 @@ sub store { trace(3); # first write out the ones for the physical repos - my @phy_repos = list_physical_repos(1); - _chdir($GL_REPO_BASE); - for my $repo (@phy_repos) { + my $phy_repos = list_phy_repos(1); + + for my $repo (@{ $phy_repos }) { store_1($repo); } @@ -228,26 +228,6 @@ sub check_subconf_repo_disallowed { return 1; } -{ - my @phy_repos = (); - - sub list_physical_repos { - trace(3); - _chdir($GL_REPO_BASE); - - # use cached value only if it exists *and* no arg was received (i.e., - # receiving *any* arg invalidates cache) - return @phy_repos if ( @phy_repos and not @_ ); - - for my $repo (`find . -name "*.git" -prune`) { - chomp($repo); - $repo =~ s(\./(.*)\.git$)($1); - push @phy_repos, $repo; - } - return @phy_repos; - } -} - sub store_1 { # warning: writes and *deletes* it from %repos and %configs my ($repo) = shift; From 1a1be8b222b6b4c7542af49c892eb84a498d1337 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 8 Mar 2012 19:50:34 +0530 Subject: [PATCH 509/850] 'gitolite list-phy-repos' added --- Gitolite/Common.pm | 1 + gitolite | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Gitolite/Common.pm b/Gitolite/Common.pm index 35a5c0f..16d25ba 100644 --- a/Gitolite/Common.pm +++ b/Gitolite/Common.pm @@ -147,6 +147,7 @@ sub sort_u { my @phy_repos = (); sub list_phy_repos { + _die "'gitolite list_phy_repos' takes no arguments" if @ARGV; trace(3); # use cached value only if it exists *and* no arg was received (i.e., diff --git a/gitolite b/gitolite index dc7fb0e..8cd39d2 100755 --- a/gitolite +++ b/gitolite @@ -14,6 +14,7 @@ The following subcommands are available; they should all respond to '-h': list-groups list all group names in conf list-users list all users/user groups in conf list-repos list all repos/repo groups in conf + list-phy-repos list all repos actually on disk list-memberships list all groups a name is a member of list-members list all members of a group @@ -29,6 +30,7 @@ use FindBin; BEGIN { $ENV{GL_BINDIR} = $FindBin::Bin; } use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; use Gitolite::Common; use strict; @@ -75,6 +77,10 @@ sub args { require Gitolite::Conf::Load; Gitolite::Conf::Load->import; print "$_\n" for ( @{ list_repos() } ); + } elsif ( $command eq 'list-phy-repos' ) { + shift @ARGV; + _chdir($GL_REPO_BASE); + print "$_\n" for ( @{ list_phy_repos() } ); } elsif ( $command eq 'list-memberships' ) { shift @ARGV; require Gitolite::Conf::Load; @@ -85,5 +91,7 @@ sub args { require Gitolite::Conf::Load; Gitolite::Conf::Load->import; print "$_\n" for ( @{ list_members() } ); + } else { + _die "unknown gitolite sub-command"; } } From 8ffc5307d673bfeadc59547231185c320ad4fd7e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 16 Mar 2012 09:59:45 +0530 Subject: [PATCH 510/850] (lotsa files affected) rc file format changed; see below The rc file used to be a bunch of variables, each one requiring to be declared before being used. While this was nice and all, it was a little cumbersome to add a new flag or option. If you disregard the "catch typos" aspect of having to predeclare variables, it's a lot more useful to have all of rc be in a hash and use any hash keys you want. There could be other uses; for instance it could hold arbitrary data that you would currently put in %ENV, without having to pollute %ENV if you don't need child tasks to inherit it. ---- NOTE: I also ran perltidy, which I don't always remember to :) --- Gitolite/Commands/QueryRc.pm | 2 +- Gitolite/Commands/Setup.pm | 18 +-- Gitolite/Conf.pm | 8 +- Gitolite/Conf/Load.pm | 33 +++--- Gitolite/Conf/Store.pm | 32 +++--- Gitolite/Hooks/PostUpdate.pm | 2 +- Gitolite/Rc.pm | 90 ++++++++------- gitolite | 2 +- gitolite-shell | 2 +- src/Gitolite/Rc.pm | 214 +++++++++++++++++++++++++++++++++++ 10 files changed, 309 insertions(+), 94 deletions(-) create mode 100644 src/Gitolite/Rc.pm diff --git a/Gitolite/Commands/QueryRc.pm b/Gitolite/Commands/QueryRc.pm index a36e4bd..4d241b4 100644 --- a/Gitolite/Commands/QueryRc.pm +++ b/Gitolite/Commands/QueryRc.pm @@ -40,7 +40,7 @@ my $all = 0; # ---------------------------------------------------------------------- sub query_rc { - trace( 1, "rc file not found; default should be " . glrc_default_filename() ) if not glrc_filename(); + trace( 1, "rc file not found; default should be " . glrc('default-filename') ) if not glrc('filename'); my @vars = args(); diff --git a/Gitolite/Commands/Setup.pm b/Gitolite/Commands/Setup.pm index aa312ad..939de8e 100644 --- a/Gitolite/Commands/Setup.pm +++ b/Gitolite/Commands/Setup.pm @@ -56,7 +56,7 @@ sub setup { sub first_run { # if the rc file could not be found, it's *definitely* a first run! - return not glrc_filename(); + return not glrc('filename'); } sub args { @@ -91,7 +91,7 @@ sub args { sub setup_glrc { trace(1); - _print( glrc_default_filename(), glrc_default_text() ); + _print( glrc('default-filename'), glrc('default-text') ); } sub setup_gladmin { @@ -99,7 +99,7 @@ sub setup_gladmin { trace( 1, $admin ); # reminder: 'admin files' are in ~/.gitolite, 'admin repo' is - # $GL_REPO_BASE/gitolite-admin.git + # $rc{GL_REPO_BASE}/gitolite-admin.git # grab the pubkey content before we chdir() away @@ -111,8 +111,8 @@ sub setup_gladmin { # set up the admin files in admin-base - _mkdir($GL_ADMIN_BASE); - _chdir($GL_ADMIN_BASE); + _mkdir( $rc{GL_ADMIN_BASE} ); + _chdir( $rc{GL_ADMIN_BASE} ); _mkdir("conf"); my $conf; @@ -132,15 +132,15 @@ sub setup_gladmin { # set up the admin repo in repo-base _chdir(); - _mkdir($GL_REPO_BASE); - _chdir($GL_REPO_BASE); + _mkdir( $rc{GL_REPO_BASE} ); + _chdir( $rc{GL_REPO_BASE} ); new_repo("gitolite-admin"); # commit the admin files to the admin repo - $ENV{GIT_WORK_TREE} = $GL_ADMIN_BASE; - _chdir("$GL_REPO_BASE/gitolite-admin.git"); + $ENV{GIT_WORK_TREE} = $rc{GL_ADMIN_BASE}; + _chdir("$rc{GL_REPO_BASE}/gitolite-admin.git"); system("git add conf/gitolite.conf"); system("git add keydir") if $pubkey; tsh_try("git config --get user.email") or tsh_run( "git config user.email $ENV{USER}\@" . `hostname` ); diff --git a/Gitolite/Conf.pm b/Gitolite/Conf.pm index 8f7e111..cffee63 100644 --- a/Gitolite/Conf.pm +++ b/Gitolite/Conf.pm @@ -34,7 +34,7 @@ sub compile { trace(3); # XXX assume we're in admin-base/conf - _chdir($GL_ADMIN_BASE); + _chdir( $rc{GL_ADMIN_BASE} ); _chdir("conf"); explode( 'gitolite.conf', 'master', \&parse ); @@ -99,7 +99,7 @@ sub parse { } } elsif ( $line =~ /^config (.+) = ?(.*)/ ) { my ( $key, $value ) = ( $1, $2 ); - my @validkeys = split( ' ', ( $GL_GITCONFIG_KEYS || '' ) ); + my @validkeys = split( ' ', ( $rc{GL_GITCONFIG_KEYS} || '' ) ); push @validkeys, "gitolite-options\\..*"; my @matched = grep { $key =~ /^$_$/ } @validkeys; # XXX move this also to add_config: _die "git config $key not allowed\ncheck GL_GITCONFIG_KEYS in the rc file for how to allow it" if (@matched < 1); @@ -123,8 +123,8 @@ sub incsub { # XXX move this to Macros... substitute HOSTNAME word if GL_HOSTNAME defined, otherwise leave as is # $include_glob =~ s/\bHOSTNAME\b/$GL_HOSTNAME/ if $GL_HOSTNAME; - # XXX g2 diff: include glob is *implicitly* from $GL_ADMIN_BASE/conf, not *explicitly* - # for my $file (glob($include_glob =~ m(^/) ? $include_glob : "$GL_ADMIN_BASE/conf/$include_glob")) { + # XXX g2 diff: include glob is *implicitly* from $rc{GL_ADMIN_BASE}/conf, not *explicitly* + # for my $file (glob($include_glob =~ m(^/) ? $include_glob : "$rc{GL_ADMIN_BASE}/conf/$include_glob")) { trace( 3, $is_subconf, $include_glob ); diff --git a/Gitolite/Conf/Load.pm b/Gitolite/Conf/Load.pm index 9367296..3237748 100644 --- a/Gitolite/Conf/Load.pm +++ b/Gitolite/Conf/Load.pm @@ -92,7 +92,7 @@ sub access { sub load_common { - _chdir("$GL_ADMIN_BASE"); + _chdir( $rc{GL_ADMIN_BASE} ); # we take an unusual approach to caching this function! # (requires that first call to load_common is before first call to load_1) @@ -118,7 +118,7 @@ sub load_1 { my $repo = shift; trace( 4, $repo ); - _chdir("$GL_REPO_BASE"); + _chdir( $rc{GL_REPO_BASE} ); if ( $repo eq $last_repo ) { $repos{$repo} = $one_repo{$repo}; @@ -172,7 +172,7 @@ sub memberships { } sub data_version_mismatch { - return $data_version ne $current_data_version; + return $data_version ne glrc('current-data-version'); } # ---------------------------------------------------------------------- @@ -192,10 +192,10 @@ Usage: gitolite list-groups load_common(); my @g = (); - while (my ($k, $v) = each ( %groups )) { - push @g, @{ $v }; + while ( my ( $k, $v ) = each(%groups) ) { + push @g, @{$v}; } - return (sort_u(\@g)); + return ( sort_u( \@g ) ); } sub list_users { @@ -213,19 +213,18 @@ Usage: gitolite list-users load_common(); - my @u = map { keys %{ $_ } } values %repos; - $total = scalar(keys %split_conf); + my @u = map { keys %{$_} } values %repos; + $total = scalar( keys %split_conf ); warn "WARNING: you have $total repos to check; this could take some time!\n" if $total > 100; for my $one ( keys %split_conf ) { load_1($one); - $count++; print STDERR "$count / $total\r" if not ( $count % 100 ) and timer(5); - push @u, map { keys %{ $_ } } values %one_repo; + $count++; print STDERR "$count / $total\r" if not( $count % 100 ) and timer(5); + push @u, map { keys %{$_} } values %one_repo; } print STDERR "\n"; - return (sort_u(\@u)); + return ( sort_u( \@u ) ); } - sub list_repos { die " @@ -241,7 +240,7 @@ Usage: gitolite list-repos my @r = keys %repos; push @r, keys %split_conf; - return (sort_u(\@r)); + return ( sort_u( \@r ) ); } sub list_memberships { @@ -258,7 +257,7 @@ Usage: gitolite list-memberships load_common(); my @m = memberships($name); - return (sort_u(\@m)); + return ( sort_u( \@m ) ); } sub list_members { @@ -276,13 +275,13 @@ Usage: gitolite list-members load_common(); my @m = (); - while (my ($k, $v) = each ( %groups )) { - for my $g ( @{ $v } ) { + while ( my ( $k, $v ) = each(%groups) ) { + for my $g ( @{$v} ) { push @m, $k if $g eq $name; } } - return (sort_u(\@m)); + return ( sort_u( \@m ) ); } # ---------------------------------------------------------------------- diff --git a/Gitolite/Conf/Store.pm b/Gitolite/Conf/Store.pm index 518a750..b99ac0b 100644 --- a/Gitolite/Conf/Store.pm +++ b/Gitolite/Conf/Store.pm @@ -145,14 +145,14 @@ sub set_subconf { sub new_repos { trace(3); - _chdir($GL_REPO_BASE); + _chdir( $rc{GL_REPO_BASE} ); # normal repos my @repos = grep { $_ =~ $REPONAME_PATT and not /^@/ } sort keys %repos; # add in members of repo groups map { push @repos, keys %{ $groups{$_} } } grep { /^@/ } keys %repos; - for my $repo ( @{ sort_u(\@repos) } ) { + for my $repo ( @{ sort_u( \@repos ) } ) { next unless $repo =~ $REPONAME_PATT; # skip repo patterns next if $repo =~ m(^\@|EXTCMD/); # skip groups and fake repos @@ -170,7 +170,7 @@ sub new_repo { _mkdir("$repo.git"); _chdir("$repo.git"); system("git init --bare >&2"); - _chdir($GL_REPO_BASE); + _chdir( $rc{GL_REPO_BASE} ); hook_1($repo); # XXX ignoring creator for now @@ -180,7 +180,7 @@ sub new_repo { sub hook_repos { trace(3); # all repos, all hooks - _chdir($GL_REPO_BASE); + _chdir( $rc{GL_REPO_BASE} ); # XXX g2 diff: we now don't care if it's a symlink -- it's upto the admin # on the server to make sure things are kosher @@ -195,14 +195,14 @@ sub store { trace(3); # first write out the ones for the physical repos - _chdir($GL_REPO_BASE); + _chdir( $rc{GL_REPO_BASE} ); my $phy_repos = list_phy_repos(1); - for my $repo (@{ $phy_repos }) { + for my $repo ( @{$phy_repos} ) { store_1($repo); } - _chdir($GL_ADMIN_BASE); + _chdir( $rc{GL_ADMIN_BASE} ); store_common(); } @@ -261,7 +261,7 @@ sub store_common { my $cc = "conf/gitolite.conf-compiled.pm"; my $compiled_fh = _open( ">", "$cc.new" ); - my $data_version = $current_data_version; + my $data_version = glrc('current-data-version'); trace( 1, "data_version = $data_version" ); print $compiled_fh Data::Dumper->Dump( [$data_version], [qw(*data_version)] ); @@ -296,20 +296,20 @@ sub store_common { # reset the gitolite supplied hooks, in case someone fiddled with # them, but only once per run if ( not $hook_reset ) { - _mkdir("$GL_ADMIN_BASE/hooks/common"); - _mkdir("$GL_ADMIN_BASE/hooks/gitolite-admin"); - _print( "$GL_ADMIN_BASE/hooks/common/update", update_hook() ); - _print( "$GL_ADMIN_BASE/hooks/gitolite-admin/post-update", post_update_hook() ); - chmod 0755, "$GL_ADMIN_BASE/hooks/common/update"; - chmod 0755, "$GL_ADMIN_BASE/hooks/gitolite-admin/post-update"; + _mkdir("$rc{GL_ADMIN_BASE}/hooks/common"); + _mkdir("$rc{GL_ADMIN_BASE}/hooks/gitolite-admin"); + _print( "$rc{GL_ADMIN_BASE}/hooks/common/update", update_hook() ); + _print( "$rc{GL_ADMIN_BASE}/hooks/gitolite-admin/post-update", post_update_hook() ); + chmod 0755, "$rc{GL_ADMIN_BASE}/hooks/common/update"; + chmod 0755, "$rc{GL_ADMIN_BASE}/hooks/gitolite-admin/post-update"; $hook_reset++; } # propagate user hooks - ln_sf( "$GL_ADMIN_BASE/hooks/common", "*", "$repo.git/hooks" ); + ln_sf( "$rc{GL_ADMIN_BASE}/hooks/common", "*", "$repo.git/hooks" ); # propagate admin hook - ln_sf( "$GL_ADMIN_BASE/hooks/gitolite-admin", "*", "$repo.git/hooks" ) if $repo eq 'gitolite-admin'; + ln_sf( "$rc{GL_ADMIN_BASE}/hooks/gitolite-admin", "*", "$repo.git/hooks" ) if $repo eq 'gitolite-admin'; # g2 diff: no "site-wide" hooks (the stuff in between gitolite hooks # and user hooks) anymore. I don't think anyone used them anyway... diff --git a/Gitolite/Hooks/PostUpdate.pm b/Gitolite/Hooks/PostUpdate.pm index 813733f..1ce07b2 100644 --- a/Gitolite/Hooks/PostUpdate.pm +++ b/Gitolite/Hooks/PostUpdate.pm @@ -27,7 +27,7 @@ sub post_update { _die "no files/dirs called 'hooks' or 'logs' are allowed" if tsh_text() =~ /^(hooks|logs)$/; { - local $ENV{GIT_WORK_TREE} = $GL_ADMIN_BASE; + local $ENV{GIT_WORK_TREE} = $rc{GL_ADMIN_BASE}; tsh_try("git checkout -f --quiet master"); } system("$ENV{GL_BINDIR}/gitolite compile"); diff --git a/Gitolite/Rc.pm b/Gitolite/Rc.pm index 94f6613..6fe0ff9 100644 --- a/Gitolite/Rc.pm +++ b/Gitolite/Rc.pm @@ -4,24 +4,14 @@ package Gitolite::Rc; # ---------------------------------------------------------------------- @EXPORT = qw( - $GL_ADMIN_BASE - $GL_REPO_BASE - - $GL_UMASK - - $GL_GITCONFIG_KEYS - - glrc_default_text - glrc_default_filename - glrc_filename + %rc + glrc $ADC_CMD_ARGS_PATT $REF_OR_FILENAME_PATT $REPONAME_PATT $REPOPATT_PATT $USERNAME_PATT - - $current_data_version ); use Exporter 'import'; @@ -32,14 +22,12 @@ use Gitolite::Common; # variables that are/could be/should be in the rc file # ---------------------------------------------------------------------- -$GL_ADMIN_BASE = "$ENV{HOME}/.gitolite"; -$GL_REPO_BASE = "$ENV{HOME}/repositories"; +$rc{GL_ADMIN_BASE} = "$ENV{HOME}/.gitolite"; +$rc{GL_REPO_BASE} = "$ENV{HOME}/repositories"; # variables that should probably never be changed # ---------------------------------------------------------------------- -$current_data_version = "3.0"; - $ADC_CMD_ARGS_PATT = qr(^[0-9a-zA-Z._\@/+:-]*$); $REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][0-9a-zA-Z._\@/+ :,-]*$); $REPONAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$); @@ -48,44 +36,56 @@ $USERNAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); # ---------------------------------------------------------------------- +my $current_data_version = "3.0"; + +my $rc = glrc('filename'); +do $rc if -r $rc; +# let values specified in rc file override our internal ones +@rc{ keys %RC } = values %RC; + +# ---------------------------------------------------------------------- + use strict; use warnings; # ---------------------------------------------------------------------- -my $rc = glrc_filename(); -do $rc if -r $rc; - +my $glrc_default_text = ''; { - my $glrc_default_text = ''; + local $/ = undef; + $glrc_default_text = ; +} - sub glrc_default_text { +sub glrc { + my $cmd = shift; + if ( $cmd eq 'default-filename' ) { + trace( 1, "..should happen only on first run" ); + return "$ENV{HOME}/.gitolite.rc"; + } elsif ( $cmd eq 'default-text' ) { trace( 1, "..should happen only on first run" ); return $glrc_default_text if $glrc_default_text; - local $/ = undef; - $glrc_default_text = ; + _die "rc file default text not set; this should not happen!"; + } elsif ( $cmd eq 'filename' ) { + # where is the rc file? + trace(4); + + # search $HOME first + return "$ENV{HOME}/.gitolite.rc" if -f "$ENV{HOME}/.gitolite.rc"; + trace( 2, "$ENV{HOME}/.gitolite.rc not found" ); + + # XXX for fedora, we can add the following line, but I would really prefer + # if ~/.gitolite.rc on each $HOME was just a symlink to /etc/gitolite.rc + # XXX return "/etc/gitolite.rc" if -f "/etc/gitolite.rc"; + + return ''; + } elsif ( $cmd eq 'current-data-version' ) { + return $current_data_version; + } else { + _die "unknown argument to glrc: $cmd"; } } -sub glrc_default_filename { - trace( 1, "..should happen only on first run" ); - return "$ENV{HOME}/.gitolite.rc"; -} - -# where is the rc file? -sub glrc_filename { - trace(4); - - # search $HOME first - return "$ENV{HOME}/.gitolite.rc" if -f "$ENV{HOME}/.gitolite.rc"; - trace( 2, "$ENV{HOME}/.gitolite.rc not found" ); - - # XXX for fedora, we can add the following line, but I would really prefer - # if ~/.gitolite.rc on each $HOME was just a symlink to /etc/gitolite.rc - # XXX return "/etc/gitolite.rc" if -f "/etc/gitolite.rc"; - - return ''; -} +# ---------------------------------------------------------------------- 1; @@ -99,8 +99,10 @@ __DATA__ # this file is in perl syntax. However, you do NOT need to know perl to edit # it; it should be fairly self-explanatory and easy to maintain -$GL_UMASK = 0077; -$GL_GITCONFIG_KEYS = ""; +%RC = ( + GL_UMASK => 0077, + GL_GITCONFIG_KEYS => "", +); # ------------------------------------------------------------------------------ # per perl rules, this should be the last line in such a file: diff --git a/gitolite b/gitolite index 8cd39d2..d8d1c3e 100755 --- a/gitolite +++ b/gitolite @@ -79,7 +79,7 @@ sub args { print "$_\n" for ( @{ list_repos() } ); } elsif ( $command eq 'list-phy-repos' ) { shift @ARGV; - _chdir($GL_REPO_BASE); + _chdir( $rc{GL_REPO_BASE} ); print "$_\n" for ( @{ list_phy_repos() } ); } elsif ( $command eq 'list-memberships' ) { shift @ARGV; diff --git a/gitolite-shell b/gitolite-shell index 479773c..59b2984 100755 --- a/gitolite-shell +++ b/gitolite-shell @@ -38,7 +38,7 @@ my $ret = access( $repo, $user, $aa, 'unknown' ); trace( 1, "access($repo, $user, $aa, 'unknown') -> $ret" ); _die $ret if $ret =~ /DENIED/; -$repo = "'$GL_REPO_BASE/$repo.git'"; +$repo = "'$rc{GL_REPO_BASE}/$repo.git'"; exec( "git", "shell", "-c", "$verb $repo" ); # ---------------------------------------------------------------------- diff --git a/src/Gitolite/Rc.pm b/src/Gitolite/Rc.pm new file mode 100644 index 0000000..e84ee0c --- /dev/null +++ b/src/Gitolite/Rc.pm @@ -0,0 +1,214 @@ +package Gitolite::Rc; + +# everything to do with 'rc'. Also defines some 'constants' +# ---------------------------------------------------------------------- + +@EXPORT = qw( + %rc + glrc + query_rc + version + + $REMOTE_COMMAND_PATT + $REF_OR_FILENAME_PATT + $REPONAME_PATT + $REPOPATT_PATT + $USERNAME_PATT +); + +use Exporter 'import'; +use Getopt::Long; + +use Gitolite::Common; + +# ---------------------------------------------------------------------- + +our %rc; + +# ---------------------------------------------------------------------- + +# variables that are/could be/should be in the rc file +# ---------------------------------------------------------------------- + +$rc{GL_BINDIR} = $ENV{GL_BINDIR}; +$rc{GL_ADMIN_BASE} = "$ENV{HOME}/.gitolite"; +$rc{GL_REPO_BASE} = "$ENV{HOME}/repositories"; + +# variables that should probably never be changed +# ---------------------------------------------------------------------- + +$REMOTE_COMMAND_PATT = qr(^[- 0-9a-zA-Z\@\%_=+:,./]*$); +$REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][0-9a-zA-Z._\@/+ :,-]*$); +$REPONAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$); +$REPOPATT_PATT = qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/,-]*$); +$USERNAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); + +# ---------------------------------------------------------------------- + +my $current_data_version = "3.0"; + +my $rc = glrc('filename'); +do $rc if -r $rc; +_die "$rc seems to be for older gitolite" if defined($GL_ADMINDIR); +# let values specified in rc file override our internal ones +@rc{ keys %RC } = values %RC; + +# testing sometimes requires all of it to be overridden silently; use an +# env var that is highly unlikely to appear in real life :) +do $ENV{G3T_RC} if exists $ENV{G3T_RC} and -r $ENV{G3T_RC}; + +# fix PATH (TODO: do it only if 'gitolite' isn't in PATH) +$ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}"; + +# ---------------------------------------------------------------------- + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +my $glrc_default_text = ''; +{ + local $/ = undef; + $glrc_default_text = ; +} + +sub glrc { + my $cmd = shift; + if ( $cmd eq 'default-filename' ) { + return "$ENV{HOME}/.gitolite.rc"; + } elsif ( $cmd eq 'default-text' ) { + return $glrc_default_text if $glrc_default_text; + _die "rc file default text not set; this should not happen!"; + } elsif ( $cmd eq 'filename' ) { + # where is the rc file? + + # search $HOME first + return "$ENV{HOME}/.gitolite.rc" if -f "$ENV{HOME}/.gitolite.rc"; + + # XXX for fedora, we can add the following line, but I would really prefer + # if ~/.gitolite.rc on each $HOME was just a symlink to /etc/gitolite.rc + # XXX return "/etc/gitolite.rc" if -f "/etc/gitolite.rc"; + + return ''; + } elsif ( $cmd eq 'current-data-version' ) { + return $current_data_version; + } else { + _die "unknown argument to glrc: $cmd"; + } +} + +# ---------------------------------------------------------------------- +# implements 'gitolite query-rc' and 'version' +# ---------------------------------------------------------------------- + +# ---------------------------------------------------------------------- + +my $all = 0; +my $nonl = 0; + +sub query_rc { + + my @vars = args(); + + no strict 'refs'; + + if ($all) { + for my $e ( sort keys %rc ) { + print "$e=" . ( defined( $rc{$e} ) ? $rc{$e} : 'undef' ) . "\n"; + } + return; + } + + print join( "\t", map { $rc{$_} || '' } @vars ) . ( $nonl ? '' : "\n" ) if @vars; +} + +sub version { + my $version = ''; + $version = '(unknown)'; + for ("$rc{GL_ADMIN_BASE}/VERSION") { + $version = slurp($_) if -r $_; + } + chomp($version); + return $version; +} + +# ---------------------------------------------------------------------- + +=for args +Usage: gitolite query-rc -a + gitolite query-rc [-n] + + -a print all variables and values + -n do not append a newline + +Example: + + gitolite query-rc GL_ADMIN_BASE UMASK + # prints "/home/git/.gitolite0077" or similar + + gitolite query-rc -a + # prints all known variables and values, one per line +=cut + +sub args { + my $help = 0; + + GetOptions( + 'all|a' => \$all, + 'nonl|n' => \$nonl, + 'help|h' => \$help, + ) or usage(); + + usage("'-a' cannot be combined with other arguments") if $all and @ARGV; + usage() if not $all and not @ARGV or $help; + return @ARGV; +} + +1; + +# ---------------------------------------------------------------------- + +__DATA__ +# configuration variables for gitolite + +# This file is in perl syntax. But you do NOT need to know perl to edit it -- +# just mind the commas and make sure the brackets and braces stay matched up! + +# (Tip: perl allows a comma after the last item in a list also!) + +%RC = ( + UMASK => 0077, + GL_GITCONFIG_KEYS => "", + + # comment out or uncomment as needed + # these will run in sequence during the conf file parse + SYNTACTIC_SUGAR => + [ + # 'continuation-lines', + ], + + # comment out or uncomment as needed + # these will run in sequence after post-update + POST_COMPILE => + [ + 'post-compile/ssh-authkeys', + ], + + # comment out or uncomment as needed + # these are available to remote users + COMMANDS => + { + 'help' => 1, + 'info' => 1, + }, +); + +# ------------------------------------------------------------------------------ +# per perl rules, this should be the last line in such a file: +1; + +# Local variables: +# mode: perl +# End: +# vim: set syn=perl: From b89ac4dd1ef405506f6a5fc4f72e70162eab57e8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 9 Mar 2012 06:22:01 +0530 Subject: [PATCH 511/850] queryrc.pm rolled into rc.pm, removed --- Gitolite/Commands/QueryRc.pm | 81 ------------------------------------ Gitolite/Rc.pm | 63 ++++++++++++++++++++++++++++ gitolite | 2 - 3 files changed, 63 insertions(+), 83 deletions(-) delete mode 100644 Gitolite/Commands/QueryRc.pm diff --git a/Gitolite/Commands/QueryRc.pm b/Gitolite/Commands/QueryRc.pm deleted file mode 100644 index 4d241b4..0000000 --- a/Gitolite/Commands/QueryRc.pm +++ /dev/null @@ -1,81 +0,0 @@ -package Gitolite::Commands::QueryRc; - -# implements 'gitolite query-rc' -# ---------------------------------------------------------------------- - -=for usage - -Usage: gitolite query-rc -a - gitolite query-rc - -Example: - - gitolite query-rc GL_ADMIN_BASE GL_UMASK - # prints "/home/git/.gitolite0077" or similar - - gitolite query-rc -a - # prints all known variables and values, one per line -=cut - -# ---------------------------------------------------------------------- - -@EXPORT = qw( - query_rc -); - -use Exporter 'import'; -use Getopt::Long; - -use lib $ENV{GL_BINDIR}; -use Gitolite::Rc; -use Gitolite::Common; - -use strict; -use warnings; - -# ---------------------------------------------------------------------- - -my $all = 0; - -# ---------------------------------------------------------------------- - -sub query_rc { - trace( 1, "rc file not found; default should be " . glrc('default-filename') ) if not glrc('filename'); - - my @vars = args(); - - no strict 'refs'; - - if ( $vars[0] eq '-a' ) { - for my $e (@Gitolite::Rc::EXPORT) { - # perl-ism warning: if you don't do this the implicit aliasing - # screws up Rc's EXPORT list - my $v = $e; - # we stop on the first non-$ var - last unless $v =~ s/^\$//; - print "$v=" . ( defined($$v) ? $$v : 'undef' ) . "\n"; - } - } - - our $GL_BINDIR = $ENV{GL_BINDIR}; - - print join( "\t", map { $$_ } grep { $$_ } @vars ) . "\n" if @vars; -} - -# ---------------------------------------------------------------------- - -sub args { - my $help = 0; - - GetOptions( - 'all|a' => \$all, - 'help|h' => \$help, - ) or usage(); - - usage("'-a' cannot be combined with other arguments") if $all and @ARGV; - return '-a' if $all; - usage() if not @ARGV or $help; - return @ARGV; -} - -1; diff --git a/Gitolite/Rc.pm b/Gitolite/Rc.pm index 6fe0ff9..47f0cdb 100644 --- a/Gitolite/Rc.pm +++ b/Gitolite/Rc.pm @@ -6,6 +6,7 @@ package Gitolite::Rc; @EXPORT = qw( %rc glrc + query_rc $ADC_CMD_ARGS_PATT $REF_OR_FILENAME_PATT @@ -15,10 +16,17 @@ package Gitolite::Rc; ); use Exporter 'import'; +use Getopt::Long; use lib $ENV{GL_BINDIR}; use Gitolite::Common; +# ---------------------------------------------------------------------- + +our %rc; + +# ---------------------------------------------------------------------- + # variables that are/could be/should be in the rc file # ---------------------------------------------------------------------- @@ -86,6 +94,61 @@ sub glrc { } # ---------------------------------------------------------------------- +# implements 'gitolite query-rc' +# ---------------------------------------------------------------------- + +=for usage + +Usage: gitolite query-rc -a + gitolite query-rc + +Example: + + gitolite query-rc GL_ADMIN_BASE GL_UMASK + # prints "/home/git/.gitolite0077" or similar + + gitolite query-rc -a + # prints all known variables and values, one per line +=cut + +# ---------------------------------------------------------------------- + +my $all = 0; + +sub query_rc { + trace( 1, "rc file not found; default should be " . glrc('default-filename') ) if not glrc('filename'); + + my @vars = args(); + + no strict 'refs'; + + if ( $vars[0] eq '-a' ) { + for my $e (sort keys %rc) { + print "$e=" . ( defined($rc{$e}) ? $rc{$e} : 'undef' ) . "\n"; + } + return; + } + + our $GL_BINDIR = $ENV{GL_BINDIR}; + + print join( "\t", map { $rc{$_} } @vars ) . "\n" if @vars; +} + +# ---------------------------------------------------------------------- + +sub args { + my $help = 0; + + GetOptions( + 'all|a' => \$all, + 'help|h' => \$help, + ) or usage(); + + usage("'-a' cannot be combined with other arguments") if $all and @ARGV; + return '-a' if $all; + usage() if not @ARGV or $help; + return @ARGV; +} 1; diff --git a/gitolite b/gitolite index d8d1c3e..f6271b8 100755 --- a/gitolite +++ b/gitolite @@ -59,8 +59,6 @@ sub args { compile(); } elsif ( $command eq 'query-rc' ) { shift @ARGV; - require Gitolite::Commands::QueryRc; - Gitolite::Commands::QueryRc->import; query_rc(); } elsif ( $command eq 'list-groups' ) { shift @ARGV; From 56be906e5d739037f640d9aa2d9cb9c46910a6de Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 9 Mar 2012 13:33:32 +0530 Subject: [PATCH 512/850] deny message change; t01 also changed accordingly --- Gitolite/Conf/Load.pm | 10 +++++----- g3-info | 2 +- gitolite-shell | 6 +++--- t/t01-basic | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Gitolite/Conf/Load.pm b/Gitolite/Conf/Load.pm index 3237748..fd117fc 100644 --- a/Gitolite/Conf/Load.pm +++ b/Gitolite/Conf/Load.pm @@ -69,13 +69,13 @@ sub access { trace( 4, "perm=$perm, refex=$refex" ); # skip 'deny' rules if the ref is not (yet) known - next if $perm eq '-' and $ref eq 'unknown'; + next if $perm eq '-' and $ref eq 'any'; - # rule matches if ref matches or ref is unknown (see gitolite-shell) - next unless $ref =~ /^$refex/ or $ref eq 'unknown'; + # rule matches if ref matches or ref is any (see gitolite-shell) + next unless $ref =~ /^$refex/ or $ref eq 'any'; trace( 3, "DENIED by $refex" ) if $perm eq '-'; - return "DENIED: $aa access to $repo by $user (rule: $refex)" if $perm eq '-'; + return "$aa $ref $repo $user DENIED by $refex" if $perm eq '-'; # $perm can be RW\+?(C|D|CD|DC)?M?. $aa can be W, +, C or D, or # any of these followed by "M". @@ -85,7 +85,7 @@ sub access { return $refex if ( $perm =~ /$aaq/ ); } trace( 3, "DENIED by fallthru" ); - return "DENIED: $aa access to $repo by $user (fallthru)"; + return "$aa $ref $repo $user DENIED by fallthru"; } # ---------------------------------------------------------------------- diff --git a/g3-info b/g3-info index d4db40e..28fa9ad 100755 --- a/g3-info +++ b/g3-info @@ -20,7 +20,7 @@ use warnings; my $user = shift or die; my $aa; -my $ref = 'unknown'; +my $ref = 'any'; my $ret; while (<>) { diff --git a/gitolite-shell b/gitolite-shell index 59b2984..46ce08b 100755 --- a/gitolite-shell +++ b/gitolite-shell @@ -31,11 +31,11 @@ sanity($repo); $ENV{GL_REPO} = $repo; my $aa = ( $verb =~ 'upload' ? 'R' : 'W' ); -# a ref of 'unknown' signifies that this is a pre-git check, where we don't +# a ref of 'any' signifies that this is a pre-git check, where we don't # yet know the ref that will be eventually pushed (and even that won't apply # if it's a read operation). See the matching code in access() for more. -my $ret = access( $repo, $user, $aa, 'unknown' ); -trace( 1, "access($repo, $user, $aa, 'unknown') -> $ret" ); +my $ret = access( $repo, $user, $aa, 'any' ); +trace( 1, "access($repo, $user, $aa, 'any') -> $ret" ); _die $ret if $ret =~ /DENIED/; $repo = "'$rc{GL_REPO_BASE}/$repo.git'"; diff --git a/t/t01-basic b/t/t01-basic index 3970308..96d9fc9 100755 --- a/t/t01-basic +++ b/t/t01-basic @@ -15,7 +15,7 @@ try " ## clone glt clone dev2 file://gitolite-admin !ok; gsh - /FATAL: DENIED: R access to gitolite-admin by dev2 .fallthru./ + /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ glt clone admin --progress file://gitolite-admin ok; gsh @@ -40,13 +40,13 @@ try " git status -s; ok; /M conf/gitolite.conf/ git commit -m t01a; ok; /master.*t01a/ glt push dev2 origin; !ok; gsh - /FATAL: DENIED: W access to gitolite-admin by dev2 .fallthru./ + /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ glt push admin origin; ok; /master -. master/ tsh empty; ok; glt push admin origin master:mm !ok; gsh - /FATAL: DENIED: W access to gitolite-admin by admin .rule: refs/heads/mm./ + /DENIED by refs/heads/mm/ /remote: error: hook declined to update refs/heads/mm/ /To file://gitolite-admin/ /remote rejected. master -. mm .hook declined./ @@ -79,7 +79,7 @@ try " ## clone cd ..; ok; glt clone u1 file://t1; !ok; gsh - /FATAL: DENIED: R access to t1 by u1 .fallthru./ + /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ glt clone u2 file://t1; ok; gsh /warning: You appear to have cloned an empty repository./ @@ -89,7 +89,7 @@ try " ## push test-commit tc1 tc2 tc2; ok; /f7153e3/ glt push u2 origin; !ok; gsh - /FATAL: DENIED: W access to t1 by u2 .fallthru./ + /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ glt push u3 origin master; ok; gsh /master -. master/ @@ -100,7 +100,7 @@ try " glt push u3 origin; !ok; gsh /rejected.*master -. master.*non-fast-forward./ glt push u3 -f origin; !ok; gsh - /FATAL: DENIED: \\+ access to t1 by u3 .fallthru./ + /DENIED by fallthru/ /remote: error: hook declined to update refs/heads/master/ /To file://t1/ /remote rejected. master -. master .hook declined./ From dfccf1b0de0e1076125891134d6bf86797ed5f0e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 9 Mar 2012 08:29:41 +0530 Subject: [PATCH 513/850] t01-basic completed, some change to test.pm as well --- Gitolite/Test.pm | 6 ++ t/t01-basic | 222 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 203 insertions(+), 25 deletions(-) diff --git a/Gitolite/Test.pm b/Gitolite/Test.pm index 6b4a0ae..bb4d0a9 100644 --- a/Gitolite/Test.pm +++ b/Gitolite/Test.pm @@ -27,6 +27,12 @@ use warnings; # required preamble for all tests try " DEF gsh = /TRACE: gsh.SOC=/ + DEF reject = /hook declined to update/; /remote rejected.*hook declined/; /error: failed to push some refs to/ + + DEF AP_1 = cd ../gitolite-admin; ok or die cant find admin repo clone; + DEF AP_2 = AP_1; git add conf keydir; ok; git commit -m %1; ok; /master.* %1/ + DEF ADMIN_PUSH = AP_2 %1; glt push admin origin; ok; gsh; /master -> master/ + ./g3-install -c admin cd tsh_tempdir; "; diff --git a/t/t01-basic b/t/t01-basic index 96d9fc9..83508a9 100755 --- a/t/t01-basic +++ b/t/t01-basic @@ -10,9 +10,9 @@ use Gitolite::Test; # ---------------------------------------------------------------------- try " - plan 74 + plan 213 - ## clone + ## subtest 1 glt clone dev2 file://gitolite-admin !ok; gsh /DENIED by fallthru/ @@ -35,23 +35,19 @@ put "conf/gitolite.conf", " "; try " - ## push + # push git add conf; ok git status -s; ok; /M conf/gitolite.conf/ git commit -m t01a; ok; /master.*t01a/ glt push dev2 origin; !ok; gsh /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ - glt push admin origin; ok; /master -. master/ + glt push admin origin; ok; /master -> master/ tsh empty; ok; glt push admin origin master:mm !ok; gsh /DENIED by refs/heads/mm/ - /remote: error: hook declined to update refs/heads/mm/ - /To file://gitolite-admin/ - /remote rejected. master -. mm .hook declined./ - /error: failed to push some refs to 'file://gitolite-admin'/ - + reject "; put "conf/gitolite.conf", " @@ -69,14 +65,10 @@ put "conf/gitolite.conf", " "; try " - ## push 2 - git add conf; ok - git status -s; ok; /M conf/gitolite.conf/ - git commit -m t01b; ok; /master.*t01b/ - glt push admin origin; ok; gsh - /master -. master/ + ## subtest 2 + ADMIN_PUSH t01b - ## clone + # clone cd ..; ok; glt clone u1 file://t1; !ok; gsh /DENIED by fallthru/ @@ -86,25 +78,205 @@ try " ls -al t1; ok; /$ENV{USER}.*$ENV{USER}.*\.git/ cd t1; ok; - ## push + # push test-commit tc1 tc2 tc2; ok; /f7153e3/ glt push u2 origin; !ok; gsh /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ glt push u3 origin master; ok; gsh - /master -. master/ + /master -> master/ - ## rewind + # rewind reset-h HEAD^; ok; /HEAD is now at 537f964 tc2/ test-tick; test-commit tc3; ok; /a691552/ glt push u3 origin; !ok; gsh - /rejected.*master -. master.*non-fast-forward./ + /rejected.*master -> master.*non-fast-forward./ glt push u3 -f origin; !ok; gsh + reject /DENIED by fallthru/ - /remote: error: hook declined to update refs/heads/master/ - /To file://t1/ - /remote rejected. master -. master .hook declined./ - /error: failed to push some refs to 'file://t1'/ glt push u4 origin +master; ok; gsh - / \\+ f7153e3...a691552 master -. master.*forced update./ + / \\+ f7153e3...a691552 master -> master.*forced update./ +"; + +put "../gitolite-admin/conf/gitolite.conf", " + \@admins = admin dev1 + repo gitolite-admin + RW+ = admin + + include 'i1.conf' +"; + +put "../gitolite-admin/conf/i1.conf", " + \@g1 = u1 + \@g2 = u2 + \@g3 = u3 + \@gaa = aa + repo \@gaa + RW+ = \@g1 + RW = \@g2 + RW+ master = \@g3 + RW master = u4 + - master = u5 + RW+ dev = u5 + RW = u5 +"; + +try " + ## subtest 3 + ADMIN_PUSH t01c + + cd ..; ok +"; + +try " + glt clone u1 file://aa; ok; gsh + cd aa; ok + test-commit set3 t1 t2 t3 t4 t5 t6 t7 t8 t9 + ok + glt push u1 origin HEAD; ok; gsh + /To file://aa/ + /\\* \\[new branch\\] HEAD -> master/ + branch dev; ok + branch foo; ok + + # u1 rewind master ok + reset-h HEAD^; ok + test-commit r1; ok + glt push u1 origin +master; ok; gsh + /To file://aa/ + /\\+ cecf671...70469f5 master -> master .forced update./ + + # u2 rewind master !ok + reset-h HEAD^; ok + test-commit r2; ok + glt push u2 origin +master; !ok; gsh + reject + /DENIED by fallthru/ + + # u3 rewind master ok + reset-h HEAD^; ok + test-commit r3; ok + glt push u3 origin +master; ok; gsh + /To file://aa/ + /\\+ 70469f5...f1e6821 master -> master .forced update./ + + # u4 push master ok + test-commit u4; ok + glt push u4 origin master; ok; gsh + /To file://aa/ + /f1e6821..d308cfb +master -> master/ + + # u4 rewind master !ok + reset-h HEAD^; ok + glt push u4 origin +master; !ok; gsh + reject + /DENIED by fallthru/ + + # u3,u4 push other branches !ok + glt push u3 origin dev; !ok; gsh + reject + /DENIED by fallthru/ + glt push u4 origin dev; !ok; gsh + reject + /DENIED by fallthru/ + glt push u3 origin foo; !ok; gsh + reject + /DENIED by fallthru/ + glt push u4 origin foo; !ok; gsh + reject + /DENIED by fallthru/ + + # clean up for next set + glt push u1 -f origin master dev foo + ok; gsh + /d308cfb...f1e6821 master -> master .forced update./ + /new branch.*dev -> dev/ + /new branch.*foo -> foo/ + + # u5 push master !ok + test-commit u5 + glt push u5 origin master; !ok; gsh + reject + /DENIED by refs/heads/master/ + + # u5 rewind dev ok + glt push u5 origin +dev^:dev + ok; gsh + /\\+ cecf671...5c8a89d dev\\^ -> dev .forced update./ + + + # u5 rewind foo !ok + glt push u5 origin +foo^:foo + !ok; gsh + reject + /remote: FATAL: \\+ refs/heads/foo aa u5 DENIED by fallthru/ + + # u5 push foo ok + git checkout foo + /Switched to branch 'foo'/ + + test-commit u5 + glt push u5 origin foo; ok; gsh + /cecf671..b27cf19 *foo -> foo/ + + # u1 delete dev ok + glt push u1 origin :dev; ok; gsh + / - \\[deleted\\] *dev/ + + # push it back + glt push u1 origin dev; ok; gsh + /\\* \\[new branch\\] *dev -> dev/ + +"; + +put "| cat >> ../gitolite-admin/conf/gitolite.conf", " + \@gr1 = r1 + repo \@gr1 + RW refs/heads/v[0-9] = u1 + RW refs/heads = tester +"; + +try " + ## subtest 4 + ADMIN_PUSH t01d + + cd ..; ok + + glt clone tester file://r1; ok; gsh + /Cloning into 'r1'.../ + cd r1; ok + test-commit r1a r1b r1c r1d r1e r1f + ok + glt push tester origin HEAD;ok; gsh + /\\* \\[new branch\\] *HEAD -> master/ + git branch v1 + glt push tester origin v1; ok; gsh + /\\* \\[new branch\\] *v1 -> v1/ + +"; + +put "| cat >> ../gitolite-admin/conf/gitolite.conf", " + \@gr2 = r2 + repo \@gr2 + RW refs/heads/v[0-9] = u1 + - refs/heads/v[0-9] = tester + RW refs/heads = tester +"; + +try " + ## subtest 5 + ADMIN_PUSH t01e + + cd ..; ok + + glt clone tester file://r2; ok; gsh + /Cloning into 'r2'.../ + cd r2; ok + test-commit r2a r2b r2c r2d r2e r2f + ok + glt push tester origin HEAD;ok; gsh + /\\* \\[new branch\\] *HEAD -> master/ + git branch v1 + glt push tester origin v1; !ok; gsh + /W refs/heads/v1 r2 tester DENIED by refs/heads/v\\[0-9\\]/ " From 4ab8db49252331785373e18ab2a3e241737cdab9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 9 Mar 2012 17:19:29 +0530 Subject: [PATCH 514/850] gitolite/commands/setup -> gitolite/setup --- Gitolite/{Commands => }/Setup.pm | 2 +- gitolite | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename Gitolite/{Commands => }/Setup.pm (99%) diff --git a/Gitolite/Commands/Setup.pm b/Gitolite/Setup.pm similarity index 99% rename from Gitolite/Commands/Setup.pm rename to Gitolite/Setup.pm index 939de8e..28d1d74 100644 --- a/Gitolite/Commands/Setup.pm +++ b/Gitolite/Setup.pm @@ -1,4 +1,4 @@ -package Gitolite::Commands::Setup; +package Gitolite::Setup; # implements 'gitolite setup' # ---------------------------------------------------------------------- diff --git a/gitolite b/gitolite index f6271b8..646d036 100755 --- a/gitolite +++ b/gitolite @@ -48,8 +48,8 @@ sub args { if ( $command eq 'setup' ) { shift @ARGV; - require Gitolite::Commands::Setup; - Gitolite::Commands::Setup->import; + require Gitolite::Setup; + Gitolite::Setup->import; setup(); } elsif ( $command eq 'compile' ) { shift @ARGV; From acb2f8fe8e3bb6f0f593bf3ff0913a74264fd8f5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 9 Mar 2012 21:23:16 +0530 Subject: [PATCH 515/850] sugar high! make it easy to handle syntactic sugar. In summary, compile now calls parse(sugar('gitolite.conf')). Details: - cleanup_conf_line went from subar.pm to common.pm - explode() and minions went from conf.pm to the new explode.pm - the callback went away; everyone just passes whole arrays around now - the new sugar() takes a filename and returns a listref - all sugar scripts take and return a listref - the first "built-in" sugar is written (setting gitweb.owner and gitweb.description) the new RC file format (of being a hash called %rc) is getting a nice workout :-) --- Gitolite/Common.pm | 15 +++- Gitolite/Conf.pm | 162 +++++++-------------------------------- Gitolite/Conf/Explode.pm | 121 +++++++++++++++++++++++++++++ Gitolite/Conf/Sugar.pm | 91 +++++++++++++--------- 4 files changed, 219 insertions(+), 170 deletions(-) create mode 100644 Gitolite/Conf/Explode.pm diff --git a/Gitolite/Common.pm b/Gitolite/Common.pm index 16d25ba..0435793 100644 --- a/Gitolite/Common.pm +++ b/Gitolite/Common.pm @@ -8,7 +8,7 @@ package Gitolite::Common; print2 dbg _mkdir _open ln_sf tsh_rc sort_u say _warn _chdir _print tsh_text list_phy_repos say2 _die slurp tsh_lines - trace tsh_try + trace cleanup_conf_line tsh_try usage tsh_run ); #>>> @@ -143,6 +143,19 @@ sub sort_u { return \@sort_u; } +sub cleanup_conf_line { + my $line = shift; + + # kill comments, but take care of "#" inside *simple* strings + $line =~ s/^((".*?"|[^#"])*)#.*/$1/; + # normalise whitespace; keeps later regexes very simple + $line =~ s/=/ = /; + $line =~ s/\s+/ /g; + $line =~ s/^ //; + $line =~ s/ $//; + return $line; +} + { my @phy_repos = (); diff --git a/Gitolite/Conf.pm b/Gitolite/Conf.pm index cffee63..2846f1f 100644 --- a/Gitolite/Conf.pm +++ b/Gitolite/Conf.pm @@ -23,13 +23,6 @@ use warnings; # ---------------------------------------------------------------------- -# 'seen' for include/subconf files -my %included = (); -# 'seen' for group names on LHS -my %prefixed_groupname = (); - -# ---------------------------------------------------------------------- - sub compile { trace(3); # XXX assume we're in admin-base/conf @@ -37,7 +30,7 @@ sub compile { _chdir( $rc{GL_ADMIN_BASE} ); _chdir("conf"); - explode( 'gitolite.conf', 'master', \&parse ); + parse(sugar('gitolite.conf')); # the order matters; new repos should be created first, to give store a # place to put the individual gl-conf files @@ -45,139 +38,42 @@ sub compile { store(); } -sub explode { - trace( 4, @_ ); - my ( $file, $subconf, $parser ) = @_; - - # $parser is a ref to a callback; if not supplied we just print - $parser ||= sub { print shift, "\n"; }; - - # seed the 'seen' list if it's empty - $included{ device_inode("conf/gitolite.conf") }++ unless %included; - - my $fh = _open( "<", $file ); - my @fh = <$fh>; - my @lines = macro_expand( "# BEGIN $file\n", @fh, "# END $file\n" ); - my $line; - while (@lines) { - $line = shift @lines; - - $line = cleanup_conf_line($line); - next unless $line =~ /\S/; - - $line = prefix_groupnames( $line, $subconf ) if $subconf ne 'master'; - - if ( $line =~ /^(include|subconf) "(.+)"$/ or $line =~ /^(include|subconf) '(.+)'$/ ) { - incsub( $1, $2, $subconf, $parser ); - } else { - # normal line, send it to the callback function - $parser->($line); - } - } -} - sub parse { - trace( 4, @_ ); - my $line = shift; + my $lines = shift; + trace(4, scalar(@$lines) . " lines incoming"); - # user or repo groups - if ( $line =~ /^(@\S+) = (.*)/ ) { - add_to_group( $1, split( ' ', $2 ) ); - } elsif ( $line =~ /^repo (.*)/ ) { - set_repolist( split( ' ', $1 ) ); - } elsif ( $line =~ /^(-|C|R|RW\+?(?:C?D?|D?C?)M?) (.* )?= (.+)/ ) { - my $perm = $1; - my @refs = parse_refs( $2 || '' ); - my @users = parse_users($3); + for my $line (@$lines) { + # user or repo groups + if ( $line =~ /^(@\S+) = (.*)/ ) { + add_to_group( $1, split( ' ', $2 ) ); + } elsif ( $line =~ /^repo (.*)/ ) { + set_repolist( split( ' ', $1 ) ); + } elsif ( $line =~ /^(-|C|R|RW\+?(?:C?D?|D?C?)M?) (.* )?= (.+)/ ) { + my $perm = $1; + my @refs = parse_refs( $2 || '' ); + my @users = parse_users($3); - # XXX what do we do? s/\bCREAT[EO]R\b/~\$creator/g for @users; + # XXX what do we do? s/\bCREAT[EO]R\b/~\$creator/g for @users; - for my $ref (@refs) { - for my $user (@users) { - add_rule( $perm, $ref, $user ); + for my $ref (@refs) { + for my $user (@users) { + add_rule( $perm, $ref, $user ); + } } - } - } elsif ( $line =~ /^config (.+) = ?(.*)/ ) { - my ( $key, $value ) = ( $1, $2 ); - my @validkeys = split( ' ', ( $rc{GL_GITCONFIG_KEYS} || '' ) ); - push @validkeys, "gitolite-options\\..*"; - my @matched = grep { $key =~ /^$_$/ } @validkeys; - # XXX move this also to add_config: _die "git config $key not allowed\ncheck GL_GITCONFIG_KEYS in the rc file for how to allow it" if (@matched < 1); - # XXX both $key and $value must satisfy a liberal but secure pattern - add_config( 1, $key, $value ); - } elsif ( $line =~ /^subconf (\S+)$/ ) { - set_subconf($1); - } else { - _warn "?? $line"; - } -} - -# ---------------------------------------------------------------------- - -sub incsub { - my $is_subconf = ( +shift eq 'subconf' ); - my ( $include_glob, $subconf, $parser ) = @_; - - _die "subconf $subconf attempting to run 'subconf'\n" if $is_subconf and $subconf ne 'master'; - - # XXX move this to Macros... substitute HOSTNAME word if GL_HOSTNAME defined, otherwise leave as is - # $include_glob =~ s/\bHOSTNAME\b/$GL_HOSTNAME/ if $GL_HOSTNAME; - - # XXX g2 diff: include glob is *implicitly* from $rc{GL_ADMIN_BASE}/conf, not *explicitly* - # for my $file (glob($include_glob =~ m(^/) ? $include_glob : "$rc{GL_ADMIN_BASE}/conf/$include_glob")) { - - trace( 3, $is_subconf, $include_glob ); - - for my $file ( glob($include_glob) ) { - _warn("included file not found: '$file'"), next unless -f $file; - _die "invalid include/subconf filename $file" unless $file =~ m(([^/]+).conf$); - my $basename = $1; - - next if already_included($file); - - if ($is_subconf) { - $parser->("subconf $basename"); - explode( $file, $basename, $parser ); - $parser->("subconf $subconf"); - # XXX g2 delegaton compat: deal with this: $subconf_seen++; + } elsif ( $line =~ /^config (.+) = ?(.*)/ ) { + my ( $key, $value ) = ( $1, $2 ); + my @validkeys = split( ' ', ( $rc{GL_GITCONFIG_KEYS} || '' ) ); + push @validkeys, "gitolite-options\\..*"; + my @matched = grep { $key =~ /^$_$/ } @validkeys; + # XXX move this also to add_config: _die "git config $key not allowed\ncheck GL_GITCONFIG_KEYS in the rc file for how to allow it" if (@matched < 1); + # XXX both $key and $value must satisfy a liberal but secure pattern + add_config( 1, $key, $value ); + } elsif ( $line =~ /^subconf (\S+)$/ ) { + set_subconf($1); } else { - explode( $file, $subconf, $parser ); + _warn "?? $line"; } } } -sub prefix_groupnames { - my ( $line, $subconf ) = @_; - - my $lhs = ''; - # save 'foo' if it's an '@foo = list' line - $lhs = $1 if $line =~ /^@(\S+) = /; - # prefix all @groups in the line - $line =~ s/(^| )(@\S+)(?= |$)/ $1 . ($prefixed_groupname{$subconf}{$2} || $2) /ge; - # now prefix the LHS and store it if needed - if ($lhs) { - $line =~ s/^@\S+ = /"\@$subconf.$lhs = "/e; - trace( 3, "prefixed_groupname.$subconf.\@$lhs = \@$subconf.$lhs" ); - } - - return $line; -} - -sub already_included { - my $file = shift; - - my $file_id = device_inode($file); - return 0 unless $included{$file_id}++; - - _warn("$file already included"); - trace( 3, "$file already included" ); - return 1; -} - -sub device_inode { - my $file = shift; - trace( 3, $file, ( stat $file )[ 0, 1 ] ); - return join( "/", ( stat $file )[ 0, 1 ] ); -} - 1; diff --git a/Gitolite/Conf/Explode.pm b/Gitolite/Conf/Explode.pm new file mode 100644 index 0000000..43a5778 --- /dev/null +++ b/Gitolite/Conf/Explode.pm @@ -0,0 +1,121 @@ +package Gitolite::Conf::Explode; + +# include/subconf processor +# ---------------------------------------------------------------------- + +@EXPORT = qw( + explode +); + +use Exporter 'import'; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Common; + +use strict; +use warnings; + +# ---------------------------------------------------------------------- + +# 'seen' for include/subconf files +my %included = (); +# 'seen' for group names on LHS +my %prefixed_groupname = (); + +sub explode { + trace( 4, @_ ); + my ( $file, $subconf, $out ) = @_; + + # seed the 'seen' list if it's empty + $included{ device_inode("conf/gitolite.conf") }++ unless %included; + + my $fh = _open( "<", $file ); + while (<$fh>) { + my $line = cleanup_conf_line($_); + next unless $line =~ /\S/; + + $line = prefix_groupnames( $line, $subconf ) if $subconf ne 'master'; + + if ( $line =~ /^(include|subconf) (\S.+)$/ ) { + incsub( $1, $2, $subconf, $out ); + } else { + # normal line, send it to the callback function + push @{$out}, $line; + } + } +} + +sub incsub { + my $is_subconf = ( +shift eq 'subconf' ); + my ( $include_glob, $subconf, $out ) = @_; + + _die "subconf $subconf attempting to run 'subconf'\n" if $is_subconf and $subconf ne 'master'; + + _die "invalid include/subconf file/glob '$include_glob'" + unless $include_glob =~ /^"(.+)"$/ + or $include_glob =~ /^'(.+)'$/; + $include_glob = $1; + + # XXX move this to Macros... substitute HOSTNAME word if GL_HOSTNAME defined, otherwise leave as is + # $include_glob =~ s/\bHOSTNAME\b/$GL_HOSTNAME/ if $GL_HOSTNAME; + + # XXX g2 diff: include glob is *implicitly* from $rc{GL_ADMIN_BASE}/conf, not *explicitly* + # for my $file (glob($include_glob =~ m(^/) ? $include_glob : "$rc{GL_ADMIN_BASE}/conf/$include_glob")) { + + trace( 3, $is_subconf, $include_glob ); + + for my $file ( glob($include_glob) ) { + _warn("included file not found: '$file'"), next unless -f $file; + _die "invalid include/subconf filename $file" unless $file =~ m(([^/]+).conf$); + my $basename = $1; + + next if already_included($file); + + if ($is_subconf) { + push @{$out}, "subconf $basename"; + explode( $file, $basename, $out ); + push @{$out}, "subconf $subconf"; + # XXX g2 delegaton compat: deal with this: $subconf_seen++; + } else { + explode( $file, $subconf, $out ); + } + } +} + +sub prefix_groupnames { + my ( $line, $subconf ) = @_; + + my $lhs = ''; + # save 'foo' if it's an '@foo = list' line + $lhs = $1 if $line =~ /^@(\S+) = /; + # prefix all @groups in the line + $line =~ s/(^| )(@\S+)(?= |$)/ $1 . ($prefixed_groupname{$subconf}{$2} || $2) /ge; + # now prefix the LHS and store it if needed + if ($lhs) { + $line =~ s/^@\S+ = /"\@$subconf.$lhs = "/e; + $prefixed_groupname{$subconf}{"\@$lhs"} = "\@$subconf.$lhs"; + trace( 3, "prefixed_groupname.$subconf.\@$lhs = \@$subconf.$lhs" ); + } + + return $line; +} + +sub already_included { + my $file = shift; + + my $file_id = device_inode($file); + return 0 unless $included{$file_id}++; + + _warn("$file already included"); + trace( 3, "$file already included" ); + return 1; +} + +sub device_inode { + my $file = shift; + trace( 3, $file, ( stat $file )[ 0, 1 ] ); + return join( "/", ( stat $file )[ 0, 1 ] ); +} + +1; + diff --git a/Gitolite/Conf/Sugar.pm b/Gitolite/Conf/Sugar.pm index 5db96f2..bfa7c5f 100644 --- a/Gitolite/Conf/Sugar.pm +++ b/Gitolite/Conf/Sugar.pm @@ -4,78 +4,97 @@ package Gitolite::Conf::Sugar; # ---------------------------------------------------------------------- @EXPORT = qw( - macro_expand - cleanup_conf_line + sugar ); use Exporter 'import'; use lib $ENV{GL_BINDIR}; -use Gitolite::Common; use Gitolite::Rc; +use Gitolite::Common; +use Gitolite::Conf::Explode; use strict; use warnings; # ---------------------------------------------------------------------- -sub macro_expand { - # site-local macros, if any, then gitolite internal macros, to munge the - # input conf line if needed +sub sugar { + # gets a filename, returns a listref - my @lines = @_; + my @lines = (); + explode(shift, 'master', \@lines); - # TODO: user macros, how to allow the user to specify them? + my $lines; + $lines = \@lines; - # cheat, to keep *our* regexes simple :) - # XXX but this also kills the special '# BEGIN filename' and '# END - # filename' lines that explode() surrounds the actual data with when it - # called macro_expand(). Right now we don't need it, but... - @lines = grep /\S/, map { cleanup_conf_line($_) } @lines; + # run through the sugar stack one by one - @lines = owner_desc(@lines); + # first, user supplied sugar: + if (exists $rc{SYNTACTIC_SUGAR}) { + if (ref($rc{SYNTACTIC_SUGAR}) ne 'ARRAY') { + _warn "bad syntax for specifying sugar scripts; see docs"; + } else { + for my $s (@{ $rc{SYNTACTIC_SUGAR} }) { + _warn "ignoring unreadable sugar script $s" if not -r $s; + do $s if -r $s; + $lines = sugar_script($lines); + $lines = [ grep /\S/, map { cleanup_conf_line($_) } @$lines ]; + } + } + } - return @lines; -} + # then our stuff: -sub cleanup_conf_line { - my $line = shift; + $lines = owner_desc($lines); + # $lines = name_vref($lines); - # kill comments, but take care of "#" inside *simple* strings - $line =~ s/^((".*?"|[^#"])*)#.*/$1/; - # normalise whitespace; keeps later regexes very simple - $line =~ s/=/ = /; - $line =~ s/\s+/ /g; - $line =~ s/^ //; - $line =~ s/ $//; - return $line; + return $lines; } sub owner_desc { - my @lines = @_; + my $lines = shift; my @ret; - for my $line (@lines) { - # reponame = "some description string" - # reponame "owner name" = "some description string" + # XXX compat breakage: (1) adding repo/owner does not automatically add an + # entry to projects.list -- we need a post-procesor for that, and (2) + # removing the 'repo' line no longer suffices to remove the config entry + # from projects.list. Maybe the post-procesor should do that as well? + + # owner = "owner name" + # -> config gitweb.owner = owner name + # description = "some long description" + # -> config gitweb.description = some long description + # category = "whatever..." + # -> config gitweb.category = whatever... + + # older formats: + # repo = "some long description" + # repo = "owner name" = "some long description" + # -> config gitweb.owner = owner name + # -> config gitweb.description = some long description + + for my $line (@$lines) { if ( $line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/ ) { my ( $repo, $owner, $desc ) = ( $1, $2, $3 ); # XXX these two checks should go into add_config # _die "bad repo name '$repo'" unless $repo =~ $REPONAME_PATT; # _die "$fragment attempting to set description for $repo" # if check_fragment_repo_disallowed( $fragment, $repo ); - push @ret, "config gitolite-options.repo-desc = $desc"; - push @ret, "config gitolite-options.repo-owner = $owner" if $owner; + push @ret, "repo $repo"; + push @ret, "config gitweb.description = $desc"; + push @ret, "config gitweb.owner = $owner" if $owner; } elsif ( $line =~ /^desc = (\S.*)/ ) { - push @ret, "config gitolite-options.repo-desc = $1"; + push @ret, "config gitweb.description = $1"; } elsif ( $line =~ /^owner = (\S.*)/ ) { - my ( $repo, $owner, $desc ) = ( $1, $2, $3 ); - push @ret, "config gitolite-options.repo-owner = $1"; + push @ret, "config gitweb.owner = $1"; + } elsif ( $line =~ /^category = (\S.*)/ ) { + push @ret, "config gitweb.category = $1"; } else { push @ret, $line; } } - return @ret; + return \@ret; } 1; From 379b0c9549faa2699d49da22fe49d3048e12f0a1 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 10 Mar 2012 18:56:29 +0530 Subject: [PATCH 516/850] install/test made easy (WARNING: read below) (1) testing is very easy, just run this from a clone t/g3-clean-install-setup-test BUT BE WARNED THIS IS DESTRUCTIVE; details in t/WARNING (2) install is equally simple; see 'INSTALL' in the main directory --- Gitolite/Rc.pm | 177 ------------------ INSTALL | 17 ++ g3-info | 35 ---- g3-install | 20 -- gitolite | 95 ---------- {Gitolite => src/Gitolite}/Common.pm | 15 +- {Gitolite => src/Gitolite}/Conf.pm | 1 - {Gitolite => src/Gitolite}/Conf/Explode.pm | 1 - {Gitolite => src/Gitolite}/Conf/Load.pm | 3 +- {Gitolite => src/Gitolite}/Conf/Store.pm | 3 +- {Gitolite => src/Gitolite}/Conf/Sugar.pm | 1 - .../Gitolite}/Hooks/PostUpdate.pm | 3 +- {Gitolite => src/Gitolite}/Hooks/Update.pm | 1 - src/Gitolite/Rc.pm | 113 ++++------- {Gitolite => src/Gitolite}/Setup.pm | 7 +- {Gitolite => src/Gitolite}/Test.pm | 12 +- {Gitolite => src/Gitolite}/Test/Tsh.pm | 0 src/gitolite | 117 ++++++------ gitolite-shell => src/gitolite-shell | 6 +- t/README | 8 + t/{t01-basic => basic.t} | 2 +- t/gitolite-receive-pack | 2 +- t/gitolite-upload-pack | 2 +- t/glt | 7 +- t/reset | 8 + 25 files changed, 162 insertions(+), 494 deletions(-) delete mode 100644 Gitolite/Rc.pm create mode 100644 INSTALL delete mode 100755 g3-info delete mode 100755 g3-install delete mode 100755 gitolite rename {Gitolite => src/Gitolite}/Common.pm (92%) rename {Gitolite => src/Gitolite}/Conf.pm (98%) rename {Gitolite => src/Gitolite}/Conf/Explode.pm (99%) rename {Gitolite => src/Gitolite}/Conf/Load.pm (99%) rename {Gitolite => src/Gitolite}/Conf/Store.pm (99%) rename {Gitolite => src/Gitolite}/Conf/Sugar.pm (99%) rename {Gitolite => src/Gitolite}/Hooks/PostUpdate.pm (94%) rename {Gitolite => src/Gitolite}/Hooks/Update.pm (99%) rename {Gitolite => src/Gitolite}/Setup.pm (96%) rename {Gitolite => src/Gitolite}/Test.pm (70%) rename {Gitolite => src/Gitolite}/Test/Tsh.pm (100%) rename gitolite-shell => src/gitolite-shell (93%) create mode 100644 t/README rename t/{t01-basic => basic.t} (99%) create mode 100755 t/reset diff --git a/Gitolite/Rc.pm b/Gitolite/Rc.pm deleted file mode 100644 index 47f0cdb..0000000 --- a/Gitolite/Rc.pm +++ /dev/null @@ -1,177 +0,0 @@ -package Gitolite::Rc; - -# everything to do with 'rc'. Also defines some 'constants' -# ---------------------------------------------------------------------- - -@EXPORT = qw( - %rc - glrc - query_rc - - $ADC_CMD_ARGS_PATT - $REF_OR_FILENAME_PATT - $REPONAME_PATT - $REPOPATT_PATT - $USERNAME_PATT -); - -use Exporter 'import'; -use Getopt::Long; - -use lib $ENV{GL_BINDIR}; -use Gitolite::Common; - -# ---------------------------------------------------------------------- - -our %rc; - -# ---------------------------------------------------------------------- - -# variables that are/could be/should be in the rc file -# ---------------------------------------------------------------------- - -$rc{GL_ADMIN_BASE} = "$ENV{HOME}/.gitolite"; -$rc{GL_REPO_BASE} = "$ENV{HOME}/repositories"; - -# variables that should probably never be changed -# ---------------------------------------------------------------------- - -$ADC_CMD_ARGS_PATT = qr(^[0-9a-zA-Z._\@/+:-]*$); -$REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][0-9a-zA-Z._\@/+ :,-]*$); -$REPONAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$); -$REPOPATT_PATT = qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/,-]*$); -$USERNAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$); - -# ---------------------------------------------------------------------- - -my $current_data_version = "3.0"; - -my $rc = glrc('filename'); -do $rc if -r $rc; -# let values specified in rc file override our internal ones -@rc{ keys %RC } = values %RC; - -# ---------------------------------------------------------------------- - -use strict; -use warnings; - -# ---------------------------------------------------------------------- - -my $glrc_default_text = ''; -{ - local $/ = undef; - $glrc_default_text = ; -} - -sub glrc { - my $cmd = shift; - if ( $cmd eq 'default-filename' ) { - trace( 1, "..should happen only on first run" ); - return "$ENV{HOME}/.gitolite.rc"; - } elsif ( $cmd eq 'default-text' ) { - trace( 1, "..should happen only on first run" ); - return $glrc_default_text if $glrc_default_text; - _die "rc file default text not set; this should not happen!"; - } elsif ( $cmd eq 'filename' ) { - # where is the rc file? - trace(4); - - # search $HOME first - return "$ENV{HOME}/.gitolite.rc" if -f "$ENV{HOME}/.gitolite.rc"; - trace( 2, "$ENV{HOME}/.gitolite.rc not found" ); - - # XXX for fedora, we can add the following line, but I would really prefer - # if ~/.gitolite.rc on each $HOME was just a symlink to /etc/gitolite.rc - # XXX return "/etc/gitolite.rc" if -f "/etc/gitolite.rc"; - - return ''; - } elsif ( $cmd eq 'current-data-version' ) { - return $current_data_version; - } else { - _die "unknown argument to glrc: $cmd"; - } -} - -# ---------------------------------------------------------------------- -# implements 'gitolite query-rc' -# ---------------------------------------------------------------------- - -=for usage - -Usage: gitolite query-rc -a - gitolite query-rc - -Example: - - gitolite query-rc GL_ADMIN_BASE GL_UMASK - # prints "/home/git/.gitolite0077" or similar - - gitolite query-rc -a - # prints all known variables and values, one per line -=cut - -# ---------------------------------------------------------------------- - -my $all = 0; - -sub query_rc { - trace( 1, "rc file not found; default should be " . glrc('default-filename') ) if not glrc('filename'); - - my @vars = args(); - - no strict 'refs'; - - if ( $vars[0] eq '-a' ) { - for my $e (sort keys %rc) { - print "$e=" . ( defined($rc{$e}) ? $rc{$e} : 'undef' ) . "\n"; - } - return; - } - - our $GL_BINDIR = $ENV{GL_BINDIR}; - - print join( "\t", map { $rc{$_} } @vars ) . "\n" if @vars; -} - -# ---------------------------------------------------------------------- - -sub args { - my $help = 0; - - GetOptions( - 'all|a' => \$all, - 'help|h' => \$help, - ) or usage(); - - usage("'-a' cannot be combined with other arguments") if $all and @ARGV; - return '-a' if $all; - usage() if not @ARGV or $help; - return @ARGV; -} - -1; - -# ---------------------------------------------------------------------- - -__DATA__ -# configuration variables for gitolite - -# PLEASE READ THE DOCUMENTATION BEFORE EDITING OR ASKING QUESTIONS - -# this file is in perl syntax. However, you do NOT need to know perl to edit -# it; it should be fairly self-explanatory and easy to maintain - -%RC = ( - GL_UMASK => 0077, - GL_GITCONFIG_KEYS => "", -); - -# ------------------------------------------------------------------------------ -# per perl rules, this should be the last line in such a file: -1; - -# Local variables: -# mode: perl -# End: -# vim: set syn=perl: diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..b1c634d --- /dev/null +++ b/INSTALL @@ -0,0 +1,17 @@ +1. Clone the repo and copy src somewhere (or leave it where it is, if you're + sure no one will 'git pull' on a running system!) + + cp -a src /some/full/path + +2. (Optional) Make a symlink for the single executable 'gitolite' to + somewhere in `$PATH` + + ln -sf /full/path/to/some/damn/place/gitolite $HOME/bin + +3. Run setup. That is, either run: + + gitolite setup -a YourName -pk /tmp/YourName.pub + + or, if you did not do step 2, run: + + /some/full/path/src/gitolite -a YourName -pk /tmp/YourName.pub diff --git a/g3-info b/g3-info deleted file mode 100755 index 28fa9ad..0000000 --- a/g3-info +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/perl - -# gitolite shell, invoked from ~/.ssh/authorized_keys -# ---------------------------------------------------------------------- - -BEGIN { - # find and set bin dir - $ENV{GL_BINDIR} = "$ENV{HOME}/bin"; -} - -use lib $ENV{GL_BINDIR}; -use Gitolite::Rc; -use Gitolite::Common; -use Gitolite::Conf::Load; - -use strict; -use warnings; - -# ---------------------------------------------------------------------- - -my $user = shift or die; -my $aa; -my $ref = 'any'; - -my $ret; -while (<>) { - chomp; - - my $perm = ''; - for $aa (qw(R W C)) { - $ret = access($_, $user, $aa, $ref); - $perm .= ( $ret =~ /DENIED/ ? " " : " $aa" ); - } - print "$perm\t$_\n" if $perm =~ /\S/; -} diff --git a/g3-install b/g3-install deleted file mode 100755 index ef40012..0000000 --- a/g3-install +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# this is specific to my test env; you may want to change it - -set -e - -cd /home/g3 - -if [ "$1" = "-c" ] -then - rm -rf .gito* gito* repositories proj* bin - mkdir bin - cp ~/.ssh/id_rsa.pub ~/.ssh/admin.pub - - cd g3; cp -a gito* Gito* t/glt t/gito* ~/bin - gitolite setup -a ${2:-admin} -pk ~/.ssh/admin.pub -else - cd g3; cp -a gito* Gito* t/glt t/gito* ~/bin - gitolite setup -fi diff --git a/gitolite b/gitolite deleted file mode 100755 index 646d036..0000000 --- a/gitolite +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/perl - -# all gitolite CLI tools run as sub-commands of this command -# ---------------------------------------------------------------------- - -=for usage -Usage: gitolite [sub-command] [options] - -The following subcommands are available; they should all respond to '-h': - - setup 1st run: initial setup; all runs: hook fixups - compile compile gitolite.conf - query-rc get values of rc variables - list-groups list all group names in conf - list-users list all users/user groups in conf - list-repos list all repos/repo groups in conf - list-phy-repos list all repos actually on disk - list-memberships list all groups a name is a member of - list-members list all members of a group - -Warnings: - - list-users is disk bound and could take a while on sites with 1000s of repos - - list-memberships does not check if the name is known; unknown names come - back with 2 answers: the name itself and '@all' -=cut - -# ---------------------------------------------------------------------- - -use FindBin; - -BEGIN { $ENV{GL_BINDIR} = $FindBin::Bin; } -use lib $ENV{GL_BINDIR}; -use Gitolite::Rc; -use Gitolite::Common; - -use strict; -use warnings; - -# ---------------------------------------------------------------------- - -args(); - -# ---------------------------------------------------------------------- - -sub args { - my ( $command, @args ) = @ARGV; - usage() if not $command or $command eq '-h'; - - if ( $command eq 'setup' ) { - shift @ARGV; - require Gitolite::Setup; - Gitolite::Setup->import; - setup(); - } elsif ( $command eq 'compile' ) { - shift @ARGV; - _die "'gitolite compile' does not take any arguments" if @ARGV; - require Gitolite::Conf; - Gitolite::Conf->import; - compile(); - } elsif ( $command eq 'query-rc' ) { - shift @ARGV; - query_rc(); - } elsif ( $command eq 'list-groups' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_groups() } ); - } elsif ( $command eq 'list-users' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_users() } ); - } elsif ( $command eq 'list-repos' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_repos() } ); - } elsif ( $command eq 'list-phy-repos' ) { - shift @ARGV; - _chdir( $rc{GL_REPO_BASE} ); - print "$_\n" for ( @{ list_phy_repos() } ); - } elsif ( $command eq 'list-memberships' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_memberships() } ); - } elsif ( $command eq 'list-members' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_members() } ); - } else { - _die "unknown gitolite sub-command"; - } -} diff --git a/Gitolite/Common.pm b/src/Gitolite/Common.pm similarity index 92% rename from Gitolite/Common.pm rename to src/Gitolite/Common.pm index 0435793..6c62403 100644 --- a/Gitolite/Common.pm +++ b/src/Gitolite/Common.pm @@ -7,7 +7,7 @@ package Gitolite::Common; @EXPORT = qw( print2 dbg _mkdir _open ln_sf tsh_rc sort_u say _warn _chdir _print tsh_text list_phy_repos - say2 _die slurp tsh_lines + say2 _die _system slurp tsh_lines trace cleanup_conf_line tsh_try usage tsh_run ); @@ -97,6 +97,19 @@ sub _chdir { chdir( $_[0] || $ENV{HOME} ) or _die "chdir $_[0] failed: $!\n"; } +sub _system { + if ( system(@_) != 0 ) { + say2 "system @_ failed"; + if ( $? == -1 ) { + die "failed to execute: $!\n"; + } elsif ( $? & 127 ) { + die "child died with signal " . ( $? & 127 ) . "\n"; + } else { + die "child exited with value " . ( $? >> 8 ) . "\n"; + } + } +} + sub _open { open( my $fh, $_[0], $_[1] ) or _die "open $_[1] failed: $!\n"; return $fh; diff --git a/Gitolite/Conf.pm b/src/Gitolite/Conf.pm similarity index 98% rename from Gitolite/Conf.pm rename to src/Gitolite/Conf.pm index 2846f1f..6c7dca6 100644 --- a/Gitolite/Conf.pm +++ b/src/Gitolite/Conf.pm @@ -12,7 +12,6 @@ package Gitolite::Conf; use Exporter 'import'; use Getopt::Long; -use lib $ENV{GL_BINDIR}; use Gitolite::Common; use Gitolite::Rc; use Gitolite::Conf::Sugar; diff --git a/Gitolite/Conf/Explode.pm b/src/Gitolite/Conf/Explode.pm similarity index 99% rename from Gitolite/Conf/Explode.pm rename to src/Gitolite/Conf/Explode.pm index 43a5778..a821dc9 100644 --- a/Gitolite/Conf/Explode.pm +++ b/src/Gitolite/Conf/Explode.pm @@ -9,7 +9,6 @@ package Gitolite::Conf::Explode; use Exporter 'import'; -use lib $ENV{GL_BINDIR}; use Gitolite::Common; use strict; diff --git a/Gitolite/Conf/Load.pm b/src/Gitolite/Conf/Load.pm similarity index 99% rename from Gitolite/Conf/Load.pm rename to src/Gitolite/Conf/Load.pm index fd117fc..1b0d199 100644 --- a/Gitolite/Conf/Load.pm +++ b/src/Gitolite/Conf/Load.pm @@ -16,7 +16,6 @@ package Gitolite::Conf::Load; use Exporter 'import'; -use lib $ENV{GL_BINDIR}; use Gitolite::Common; use Gitolite::Rc; @@ -108,7 +107,7 @@ sub load_common { _die "parse $cc failed: " . ( $! or $@ ) unless do $cc; if ( data_version_mismatch() ) { - system("gitolite setup"); + _system("gitolite setup"); _die "parse $cc failed: " . ( $! or $@ ) unless do $cc; _die "data version update failed; this is serious" if data_version_mismatch(); } diff --git a/Gitolite/Conf/Store.pm b/src/Gitolite/Conf/Store.pm similarity index 99% rename from Gitolite/Conf/Store.pm rename to src/Gitolite/Conf/Store.pm index b99ac0b..37824c7 100644 --- a/Gitolite/Conf/Store.pm +++ b/src/Gitolite/Conf/Store.pm @@ -22,7 +22,6 @@ use Data::Dumper; $Data::Dumper::Indent = 1; $Data::Dumper::Sortkeys = 1; -use lib $ENV{GL_BINDIR}; use Gitolite::Common; use Gitolite::Rc; use Gitolite::Hooks::Update; @@ -169,7 +168,7 @@ sub new_repo { _mkdir("$repo.git"); _chdir("$repo.git"); - system("git init --bare >&2"); + _system("git init --bare >&2"); _chdir( $rc{GL_REPO_BASE} ); hook_1($repo); diff --git a/Gitolite/Conf/Sugar.pm b/src/Gitolite/Conf/Sugar.pm similarity index 99% rename from Gitolite/Conf/Sugar.pm rename to src/Gitolite/Conf/Sugar.pm index bfa7c5f..932928d 100644 --- a/Gitolite/Conf/Sugar.pm +++ b/src/Gitolite/Conf/Sugar.pm @@ -9,7 +9,6 @@ package Gitolite::Conf::Sugar; use Exporter 'import'; -use lib $ENV{GL_BINDIR}; use Gitolite::Rc; use Gitolite::Common; use Gitolite::Conf::Explode; diff --git a/Gitolite/Hooks/PostUpdate.pm b/src/Gitolite/Hooks/PostUpdate.pm similarity index 94% rename from Gitolite/Hooks/PostUpdate.pm rename to src/Gitolite/Hooks/PostUpdate.pm index 1ce07b2..ef8e522 100644 --- a/Gitolite/Hooks/PostUpdate.pm +++ b/src/Gitolite/Hooks/PostUpdate.pm @@ -10,7 +10,6 @@ package Gitolite::Hooks::PostUpdate; use Exporter 'import'; -use lib $ENV{GL_BINDIR}; use Gitolite::Rc; use Gitolite::Common; @@ -30,7 +29,7 @@ sub post_update { local $ENV{GIT_WORK_TREE} = $rc{GL_ADMIN_BASE}; tsh_try("git checkout -f --quiet master"); } - system("$ENV{GL_BINDIR}/gitolite compile"); + _system("$ENV{GL_BINDIR}/gitolite compile"); exit 0; } diff --git a/Gitolite/Hooks/Update.pm b/src/Gitolite/Hooks/Update.pm similarity index 99% rename from Gitolite/Hooks/Update.pm rename to src/Gitolite/Hooks/Update.pm index 2c60914..488a3ec 100644 --- a/Gitolite/Hooks/Update.pm +++ b/src/Gitolite/Hooks/Update.pm @@ -10,7 +10,6 @@ package Gitolite::Hooks::Update; use Exporter 'import'; -use lib $ENV{GL_BINDIR}; use Gitolite::Common; use Gitolite::Conf::Load; diff --git a/src/Gitolite/Rc.pm b/src/Gitolite/Rc.pm index e84ee0c..18cc6c5 100644 --- a/src/Gitolite/Rc.pm +++ b/src/Gitolite/Rc.pm @@ -7,9 +7,8 @@ package Gitolite::Rc; %rc glrc query_rc - version - $REMOTE_COMMAND_PATT + $ADC_CMD_ARGS_PATT $REF_OR_FILENAME_PATT $REPONAME_PATT $REPOPATT_PATT @@ -37,7 +36,7 @@ $rc{GL_REPO_BASE} = "$ENV{HOME}/repositories"; # variables that should probably never be changed # ---------------------------------------------------------------------- -$REMOTE_COMMAND_PATT = qr(^[- 0-9a-zA-Z\@\%_=+:,./]*$); +$ADC_CMD_ARGS_PATT = qr(^[0-9a-zA-Z._\@/+:-]*$); $REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][0-9a-zA-Z._\@/+ :,-]*$); $REPONAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$); $REPOPATT_PATT = qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/,-]*$); @@ -49,17 +48,9 @@ my $current_data_version = "3.0"; my $rc = glrc('filename'); do $rc if -r $rc; -_die "$rc seems to be for older gitolite" if defined($GL_ADMINDIR); # let values specified in rc file override our internal ones @rc{ keys %RC } = values %RC; -# testing sometimes requires all of it to be overridden silently; use an -# env var that is highly unlikely to appear in real life :) -do $ENV{G3T_RC} if exists $ENV{G3T_RC} and -r $ENV{G3T_RC}; - -# fix PATH (TODO: do it only if 'gitolite' isn't in PATH) -$ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}"; - # ---------------------------------------------------------------------- use strict; @@ -76,15 +67,19 @@ my $glrc_default_text = ''; sub glrc { my $cmd = shift; if ( $cmd eq 'default-filename' ) { + trace( 1, "..should happen only on first run" ); return "$ENV{HOME}/.gitolite.rc"; } elsif ( $cmd eq 'default-text' ) { + trace( 1, "..should happen only on first run" ); return $glrc_default_text if $glrc_default_text; _die "rc file default text not set; this should not happen!"; } elsif ( $cmd eq 'filename' ) { # where is the rc file? + trace(4); # search $HOME first return "$ENV{HOME}/.gitolite.rc" if -f "$ENV{HOME}/.gitolite.rc"; + trace( 2, "$ENV{HOME}/.gitolite.rc not found" ); # XXX for fedora, we can add the following line, but I would really prefer # if ~/.gitolite.rc on each $HOME was just a symlink to /etc/gitolite.rc @@ -99,68 +94,56 @@ sub glrc { } # ---------------------------------------------------------------------- -# implements 'gitolite query-rc' and 'version' +# implements 'gitolite query-rc' # ---------------------------------------------------------------------- -# ---------------------------------------------------------------------- +=for usage -my $all = 0; -my $nonl = 0; - -sub query_rc { - - my @vars = args(); - - no strict 'refs'; - - if ($all) { - for my $e ( sort keys %rc ) { - print "$e=" . ( defined( $rc{$e} ) ? $rc{$e} : 'undef' ) . "\n"; - } - return; - } - - print join( "\t", map { $rc{$_} || '' } @vars ) . ( $nonl ? '' : "\n" ) if @vars; -} - -sub version { - my $version = ''; - $version = '(unknown)'; - for ("$rc{GL_ADMIN_BASE}/VERSION") { - $version = slurp($_) if -r $_; - } - chomp($version); - return $version; -} - -# ---------------------------------------------------------------------- - -=for args Usage: gitolite query-rc -a - gitolite query-rc [-n] - - -a print all variables and values - -n do not append a newline + gitolite query-rc Example: - gitolite query-rc GL_ADMIN_BASE UMASK + gitolite query-rc GL_ADMIN_BASE GL_UMASK # prints "/home/git/.gitolite0077" or similar gitolite query-rc -a # prints all known variables and values, one per line =cut +# ---------------------------------------------------------------------- + +my $all = 0; + +sub query_rc { + trace( 1, "rc file not found; default should be " . glrc('default-filename') ) if not glrc('filename'); + + my @vars = args(); + + no strict 'refs'; + + if ( $vars[0] eq '-a' ) { + for my $e (sort keys %rc) { + print "$e=" . ( defined($rc{$e}) ? $rc{$e} : 'undef' ) . "\n"; + } + return; + } + + print join( "\t", map { $rc{$_} } @vars ) . "\n" if @vars; +} + +# ---------------------------------------------------------------------- + sub args { my $help = 0; GetOptions( 'all|a' => \$all, - 'nonl|n' => \$nonl, 'help|h' => \$help, ) or usage(); usage("'-a' cannot be combined with other arguments") if $all and @ARGV; + return '-a' if $all; usage() if not $all and not @ARGV or $help; return @ARGV; } @@ -172,36 +155,14 @@ sub args { __DATA__ # configuration variables for gitolite -# This file is in perl syntax. But you do NOT need to know perl to edit it -- -# just mind the commas and make sure the brackets and braces stay matched up! +# PLEASE READ THE DOCUMENTATION BEFORE EDITING OR ASKING QUESTIONS -# (Tip: perl allows a comma after the last item in a list also!) +# this file is in perl syntax. However, you do NOT need to know perl to edit +# it; it should be fairly self-explanatory and easy to maintain %RC = ( UMASK => 0077, GL_GITCONFIG_KEYS => "", - - # comment out or uncomment as needed - # these will run in sequence during the conf file parse - SYNTACTIC_SUGAR => - [ - # 'continuation-lines', - ], - - # comment out or uncomment as needed - # these will run in sequence after post-update - POST_COMPILE => - [ - 'post-compile/ssh-authkeys', - ], - - # comment out or uncomment as needed - # these are available to remote users - COMMANDS => - { - 'help' => 1, - 'info' => 1, - }, ); # ------------------------------------------------------------------------------ diff --git a/Gitolite/Setup.pm b/src/Gitolite/Setup.pm similarity index 96% rename from Gitolite/Setup.pm rename to src/Gitolite/Setup.pm index 28d1d74..b42e0fc 100644 --- a/Gitolite/Setup.pm +++ b/src/Gitolite/Setup.pm @@ -28,7 +28,6 @@ Later runs: use Exporter 'import'; use Getopt::Long; -use lib $ENV{GL_BINDIR}; use Gitolite::Rc; use Gitolite::Common; use Gitolite::Conf::Store; @@ -47,7 +46,7 @@ sub setup { setup_gladmin( $admin, $pubkey, $argv ); } - system("$ENV{GL_BINDIR}/gitolite compile"); + _system("$ENV{GL_BINDIR}/gitolite compile"); hook_repos(); # all of them, just to be sure } @@ -141,8 +140,8 @@ sub setup_gladmin { $ENV{GIT_WORK_TREE} = $rc{GL_ADMIN_BASE}; _chdir("$rc{GL_REPO_BASE}/gitolite-admin.git"); - system("git add conf/gitolite.conf"); - system("git add keydir") if $pubkey; + _system("git add conf/gitolite.conf"); + _system("git add keydir") if $pubkey; tsh_try("git config --get user.email") or tsh_run( "git config user.email $ENV{USER}\@" . `hostname` ); tsh_try("git config --get user.name") or tsh_run( "git config user.name '$ENV{USER} on '" . `hostname` ); tsh_try("git diff --cached --quiet") diff --git a/Gitolite/Test.pm b/src/Gitolite/Test.pm similarity index 70% rename from Gitolite/Test.pm rename to src/Gitolite/Test.pm index bb4d0a9..dcaf3fe 100644 --- a/Gitolite/Test.pm +++ b/src/Gitolite/Test.pm @@ -7,6 +7,7 @@ package Gitolite::Test; @EXPORT = qw( try put + text ); #>>> use Exporter 'import'; @@ -17,6 +18,7 @@ BEGIN { require Gitolite::Test::Tsh; *{'try'} = \&Tsh::try; *{'put'} = \&Tsh::put; + *{'text'} = \&Tsh::text; } use strict; @@ -30,11 +32,15 @@ try " DEF reject = /hook declined to update/; /remote rejected.*hook declined/; /error: failed to push some refs to/ DEF AP_1 = cd ../gitolite-admin; ok or die cant find admin repo clone; - DEF AP_2 = AP_1; git add conf keydir; ok; git commit -m %1; ok; /master.* %1/ + DEF AP_2 = AP_1; git add conf ; ok; git commit -m %1; ok; /master.* %1/ DEF ADMIN_PUSH = AP_2 %1; glt push admin origin; ok; gsh; /master -> master/ - ./g3-install -c admin + mkdir -p $ENV{HOME}/bin + ln -sf $ENV{PWD}/src/gitolite $ENV{PWD}/t/glt ~/bin + cd; rm -vrf .gito* gito* repositories + cd tsh_tempdir; -"; + gitolite setup -a admin +" or die "could not setup the test environment; errors:\n\n" . text() . "\n\n"; 1; diff --git a/Gitolite/Test/Tsh.pm b/src/Gitolite/Test/Tsh.pm similarity index 100% rename from Gitolite/Test/Tsh.pm rename to src/Gitolite/Test/Tsh.pm diff --git a/src/gitolite b/src/gitolite index c6a1f54..0457cc2 100755 --- a/src/gitolite +++ b/src/gitolite @@ -3,17 +3,14 @@ # all gitolite CLI tools run as sub-commands of this command # ---------------------------------------------------------------------- -=for args -Usage: gitolite [] [] +=for usage +Usage: gitolite [sub-command] [options] -The following built-in subcommands are available; they should all respond to -'-h' if you want further details on each: +The following subcommands are available; they should all respond to '-h': setup 1st run: initial setup; all runs: hook fixups compile compile gitolite.conf - query-rc get values of rc variables - list-groups list all group names in conf list-users list all users/user groups in conf list-repos list all repos/repo groups in conf @@ -25,10 +22,6 @@ Warnings: - list-users is disk bound and could take a while on sites with 1000s of repos - list-memberships does not check if the name is known; unknown names come back with 2 answers: the name itself and '@all' - -In addition, running 'gitolite help' should give you a list of custom commands -available. They may or may not respond to '-h', depending on how they were -written. =cut # ---------------------------------------------------------------------- @@ -45,62 +38,58 @@ use warnings; # ---------------------------------------------------------------------- -my ( $command, @args ) = @ARGV; -gl_log( 'gitolite', @ARGV ) if -d $rc{GL_ADMIN_BASE}; args(); -# the first two commands need options via @ARGV, as they have their own -# GetOptions calls and older perls don't have 'GetOptionsFromArray' - -if ( $command eq 'setup' ) { - shift @ARGV; - require Gitolite::Setup; - Gitolite::Setup->import; - setup(); - -} elsif ( $command eq 'query-rc' ) { - shift @ARGV; - query_rc(); - -# the rest don't need @ARGV per se - -} elsif ( $command eq 'compile' ) { - require Gitolite::Conf; - Gitolite::Conf->import; - compile(@args); - -} elsif ( $command eq 'trigger' ) { - trigger(@args); - -} elsif ( -x "$rc{GL_BINDIR}/commands/$command" ) { - trace( 2, "attempting gitolite command $command" ); - run_command( $command, @args ); - -} elsif ( $command eq 'list-phy-repos' ) { - _chdir( $rc{GL_REPO_BASE} ); - print "$_\n" for ( @{ list_phy_repos(@args) } ); - -} elsif ( $command =~ /^list-/ ) { - trace( 2, "attempting lister command $command" ); - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - my $fn = lister_dispatch($command); - print "$_\n" for ( @{ $fn->(@args) } ); - -} else { - _die "unknown gitolite sub-command"; -} - -sub args { - usage() if not $command or $command eq '-h'; -} - # ---------------------------------------------------------------------- -sub run_command { - my $pgm = shift; - my $fullpath = "$ENV{GL_BINDIR}/commands/$pgm"; - _die "$pgm not found or not executable" if not -x $fullpath; - _system( $fullpath, @_ ); - exit 0; +sub args { + my ( $command, @args ) = @ARGV; + usage() if not $command or $command eq '-h'; + + if ( $command eq 'setup' ) { + shift @ARGV; + require Gitolite::Setup; + Gitolite::Setup->import; + setup(); + } elsif ( $command eq 'compile' ) { + shift @ARGV; + _die "'gitolite compile' does not take any arguments" if @ARGV; + require Gitolite::Conf; + Gitolite::Conf->import; + compile(); + } elsif ( $command eq 'query-rc' ) { + shift @ARGV; + query_rc(); + } elsif ( $command eq 'list-groups' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_groups() } ); + } elsif ( $command eq 'list-users' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_users() } ); + } elsif ( $command eq 'list-repos' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_repos() } ); + } elsif ( $command eq 'list-phy-repos' ) { + shift @ARGV; + _chdir( $rc{GL_REPO_BASE} ); + print "$_\n" for ( @{ list_phy_repos() } ); + } elsif ( $command eq 'list-memberships' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_memberships() } ); + } elsif ( $command eq 'list-members' ) { + shift @ARGV; + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + print "$_\n" for ( @{ list_members() } ); + } else { + _die "unknown gitolite sub-command"; + } } diff --git a/gitolite-shell b/src/gitolite-shell similarity index 93% rename from gitolite-shell rename to src/gitolite-shell index 46ce08b..d7f6a19 100755 --- a/gitolite-shell +++ b/src/gitolite-shell @@ -3,11 +3,9 @@ # gitolite shell, invoked from ~/.ssh/authorized_keys # ---------------------------------------------------------------------- -BEGIN { - # find and set bin dir - $0 =~ m|^(/)?(.*)/| and $ENV{GL_BINDIR} = ( $1 || "$ENV{PWD}/" ) . $2; -} +use FindBin; +BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin; } use lib $ENV{GL_BINDIR}; use Gitolite::Rc; use Gitolite::Common; diff --git a/t/README b/t/README new file mode 100644 index 0000000..c863b41 --- /dev/null +++ b/t/README @@ -0,0 +1,8 @@ +WARNING: THE TEST SUITE DELETES STUFF FIRST! + +Testing gitolite3 is now one command after the clone: + + prove + +But because it starts by cleaning the slate, it's best to do it on a spare +userid that you are ok to lose data on. diff --git a/t/t01-basic b/t/basic.t similarity index 99% rename from t/t01-basic rename to t/basic.t index 83508a9..96c4012 100755 --- a/t/t01-basic +++ b/t/basic.t @@ -3,7 +3,7 @@ use strict; use warnings; # this is hardcoded; change it if needed -use lib "$ENV{HOME}/bin"; +use lib "src"; use Gitolite::Test; # basic tests diff --git a/t/gitolite-receive-pack b/t/gitolite-receive-pack index 48c7428..a4cc5be 100755 --- a/t/gitolite-receive-pack +++ b/t/gitolite-receive-pack @@ -9,4 +9,4 @@ $repo =~ s/\.git$//; my $user = $ENV{G3T_USER} || 'no-such-user'; $ENV{SSH_ORIGINAL_COMMAND} = "git-receive-pack '$repo'"; -exec( "$ENV{HOME}/bin/gitolite-shell", $user ); +exec( "$ENV{GL_BINDIR}/../src/gitolite-shell", $user ); diff --git a/t/gitolite-upload-pack b/t/gitolite-upload-pack index 8888abb..5981f17 100755 --- a/t/gitolite-upload-pack +++ b/t/gitolite-upload-pack @@ -9,4 +9,4 @@ $repo =~ s/\.git$//; my $user = $ENV{G3T_USER} || 'no-such-user'; $ENV{SSH_ORIGINAL_COMMAND} = "git-upload-pack '$repo'"; -exec( "$ENV{HOME}/bin/gitolite-shell", $user ); +exec( "$ENV{GL_BINDIR}/../src/gitolite-shell", $user ); diff --git a/t/glt b/t/glt index b5704f5..45e7b19 100755 --- a/t/glt +++ b/t/glt @@ -2,6 +2,9 @@ use strict; use warnings; +use FindBin; +BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin; } + print STDERR "TRACE: glt(", join( ")(", @ARGV ), ")\n"; my $cmd = shift or die "need command"; @@ -10,9 +13,9 @@ my $rc; $ENV{G3T_USER} = $user; if ( $cmd eq 'push' ) { - $rc = system( "git", $cmd, "--receive-pack=$ENV{HOME}/bin/gitolite-receive-pack", @ARGV ); + $rc = system( "git", $cmd, "--receive-pack=$ENV{GL_BINDIR}/gitolite-receive-pack", @ARGV ); } else { - $rc = system( "git", $cmd, "--upload-pack=$ENV{HOME}/bin/gitolite-upload-pack", @ARGV ); + $rc = system( "git", $cmd, "--upload-pack=$ENV{GL_BINDIR}/gitolite-upload-pack", @ARGV ); } if ( $? == -1 ) { diff --git a/t/reset b/t/reset new file mode 100755 index 0000000..8c5dcbf --- /dev/null +++ b/t/reset @@ -0,0 +1,8 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; +try 'put'; From a0305ec029c93474f7c4cb09d27b25e1ba2dd428 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 10 Mar 2012 13:56:02 +0530 Subject: [PATCH 517/850] sugar 'option'; see below option foo = bar -> config gitolite-options.foo = bar --- src/Gitolite/Conf/Sugar.pm | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Gitolite/Conf/Sugar.pm b/src/Gitolite/Conf/Sugar.pm index 932928d..881cbcf 100644 --- a/src/Gitolite/Conf/Sugar.pm +++ b/src/Gitolite/Conf/Sugar.pm @@ -45,12 +45,30 @@ sub sugar { # then our stuff: + $lines = option($lines); $lines = owner_desc($lines); # $lines = name_vref($lines); return $lines; } +sub option { + my $lines = shift; + my @ret; + + # option foo = bar + # -> config gitolite-options.foo = bar + + for my $line (@$lines) { + if ( $line =~ /^option (\S+) = (\S.*)/ ) { + push @ret, "config gitolite-options.$1 = $2"; + } else { + push @ret, $line; + } + } + return \@ret; +} + sub owner_desc { my $lines = shift; my @ret; From 0fdd80f4871102f7f6312ffa84cc2e490b2df09d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 10 Mar 2012 18:57:04 +0530 Subject: [PATCH 518/850] (tests) added a module test for explode plus a helper function to Test.pm (helps while developing tests) --- src/Gitolite/Test.pm | 8 ++++ t/m-explode.t | 102 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100755 t/m-explode.t diff --git a/src/Gitolite/Test.pm b/src/Gitolite/Test.pm index dcaf3fe..8251c41 100644 --- a/src/Gitolite/Test.pm +++ b/src/Gitolite/Test.pm @@ -8,6 +8,7 @@ package Gitolite::Test; try put text + dump ); #>>> use Exporter 'import'; @@ -43,4 +44,11 @@ try " gitolite setup -a admin " or die "could not setup the test environment; errors:\n\n" . text() . "\n\n"; +sub dump { + use Data::Dumper; + for my $i (@_) { + print STDERR "DBG: " . Dumper($i); + } +} + 1; diff --git a/t/m-explode.t b/t/m-explode.t new file mode 100755 index 0000000..aef337f --- /dev/null +++ b/t/m-explode.t @@ -0,0 +1,102 @@ +#!/usr/bin/perl +use strict; +use warnings; +use 5.10.0; + +use Test; +BEGIN { plan tests => + 2 +} + +use lib "$ENV{PWD}/src"; +use Gitolite::Test; +use Gitolite::Conf::Explode; + +my @out; +my @out2; + +warn " + <<< expect a couple of warnings about already included files >>> +"; + +# test 1 -- space normalisation + + put "foo", " + foo line 1 + foo=line 2 + + + foo 3 + "; + @out = (); + explode("foo", 'master', \@out); + @out2 = ( + 'foo line 1', + 'foo = line 2', + 'foo 3', + ); + + ok(@out ~~ @out2); + +# test 2 -- include/subconf processing + + put "foo", " + foo line 1 + \@fog=line 2 + include \"bar.conf\" + + foo line=5 + subconf \"subs/baz.conf\" + include \"bar.conf\" + foo line=7 + include \"bazup.conf\" + "; + + put "bar.conf", " + \@brg=line 1 + + bar line 3 + "; + + mkdir("subs"); + + put "subs/baz.conf", " + \@bzg = line 1 + + include \"subs/baz2.conf\" + + baz=line 3 + "; + + put "subs/baz2.conf", " + baz2 line 1 + baz2 line 2 + include \"bazup.conf\" + baz2 line 4 + "; + + put "bazup.conf", " + whatever... + "; + + @out = (); + explode("foo", 'master', \@out); + + @out2 = ( + 'foo line 1', + '@fog = line 2', + '@brg = line 1', + 'bar line 3', + 'foo line = 5', + 'subconf baz', + '@baz.bzg = line 1', + 'baz2 line 1', + 'baz2 line 2', + 'whatever...', + 'baz2 line 4', + 'baz = line 3', + 'subconf master', + 'foo line = 7' + ); + + ok(@out ~~ @out2); From a9d5adcd106329ab1802e0197ffea44d479e0649 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 10 Mar 2012 19:43:42 +0530 Subject: [PATCH 519/850] example sugar script 'continuation-lines' added --- src/Gitolite/Conf/Sugar.pm | 24 +++++++++++++++--- src/Gitolite/Rc.pm | 9 ++++++- src/syntactic-sugar/continuation-lines | 34 ++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) create mode 100755 src/syntactic-sugar/continuation-lines diff --git a/src/Gitolite/Conf/Sugar.pm b/src/Gitolite/Conf/Sugar.pm index 881cbcf..dbf0926 100644 --- a/src/Gitolite/Conf/Sugar.pm +++ b/src/Gitolite/Conf/Sugar.pm @@ -1,3 +1,16 @@ +# and now for something completely different... + +package SugarBox; + +sub run_sugar_script { + my ($ss, $lref) = @_; + do $ss if -x $ss; + $lref = sugar_script($lref); + return $lref; +} + +# ---------------------------------------------------------------------- + package Gitolite::Conf::Sugar; # syntactic sugar for the conf file, including site-local macros @@ -35,9 +48,14 @@ sub sugar { _warn "bad syntax for specifying sugar scripts; see docs"; } else { for my $s (@{ $rc{SYNTACTIC_SUGAR} }) { - _warn "ignoring unreadable sugar script $s" if not -r $s; - do $s if -r $s; - $lines = sugar_script($lines); + + # perl-ism; apart from keeping the full path separate from the + # simple name, this also protects %rc from change by implicit + # aliasing, which would happen if you touched $s itself + my $sfp = "$ENV{GL_BINDIR}/syntactic-sugar/$s"; + + _warn("skipped sugar script '$s'"), next if not -x $sfp; + $lines = SugarBox::run_sugar_script($sfp, $lines); $lines = [ grep /\S/, map { cleanup_conf_line($_) } @$lines ]; } } diff --git a/src/Gitolite/Rc.pm b/src/Gitolite/Rc.pm index 18cc6c5..0b81bf8 100644 --- a/src/Gitolite/Rc.pm +++ b/src/Gitolite/Rc.pm @@ -158,11 +158,18 @@ __DATA__ # PLEASE READ THE DOCUMENTATION BEFORE EDITING OR ASKING QUESTIONS # this file is in perl syntax. However, you do NOT need to know perl to edit -# it; it should be fairly self-explanatory and easy to maintain +# it; it should be fairly self-explanatory and easy to maintain. Just mind +# the commas, make sure the brackets and braces stay matched up! %RC = ( UMASK => 0077, GL_GITCONFIG_KEYS => "", + + # uncomment as needed + SYNTACTIC_SUGAR => + [ + # 'continuation-lines', + ] ); # ------------------------------------------------------------------------------ diff --git a/src/syntactic-sugar/continuation-lines b/src/syntactic-sugar/continuation-lines new file mode 100755 index 0000000..1d25379 --- /dev/null +++ b/src/syntactic-sugar/continuation-lines @@ -0,0 +1,34 @@ +# vim: syn=perl: + +# "sugar script" (syntactic sugar helper) for gitolite3 + +# Enabling this script in the rc file allows you to use back-slash escaped +# continuation lines, like in C or shell etc. + +# This script also serves as an example "sugar script" if you want to write +# your own (and maybe send them to me). A "sugar script" in gitolite will be +# executed via a perl 'do' and is expected to contain one function called +# 'sugar_script'. This function should take a listref and return a listref. +# Each item in the list is one line. There are NO newlines; g3 kills them off +# fairly early in the process. + +# If you're not familiar with perl please do not try this. Ask me to write +# you a sugar script instead. + +sub sugar_script { + my $lines = shift; + + my @out = (); + my $keep = ''; + for my $l (@$lines) { + if ($l =~ s/\\$//) { + $keep .= $l; + } else { + $l = $keep . $l if $keep; + $keep = ''; + push @out, $l; + } + } + + return \@out; +} From 877f6eb31bc75bc1b5bffb38a1b603874bfae451 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 10 Mar 2012 20:30:14 +0530 Subject: [PATCH 520/850] catch older gitolite.rc and die gracefully --- src/Gitolite/Rc.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Gitolite/Rc.pm b/src/Gitolite/Rc.pm index 0b81bf8..7ed5cef 100644 --- a/src/Gitolite/Rc.pm +++ b/src/Gitolite/Rc.pm @@ -48,6 +48,7 @@ my $current_data_version = "3.0"; my $rc = glrc('filename'); do $rc if -r $rc; +_die "$rc seems to be for older gitolite" if defined($GL_ADMINDIR); # let values specified in rc file override our internal ones @rc{ keys %RC } = values %RC; From 56cda99edd13bb8ae13fe517b0e800f93665626e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 10 Mar 2012 22:57:01 +0530 Subject: [PATCH 521/850] fixup CWD in access(); see below Calling access() changes the CWD to $GL_REPO_BASE! This causes a problem in the update script -- you're suddenly in the wrong directory after calling access()! This is actually happening inside load_1(), so fix that. --- src/Gitolite/Conf/Load.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Gitolite/Conf/Load.pm b/src/Gitolite/Conf/Load.pm index 1b0d199..171927e 100644 --- a/src/Gitolite/Conf/Load.pm +++ b/src/Gitolite/Conf/Load.pm @@ -117,7 +117,7 @@ sub load_1 { my $repo = shift; trace( 4, $repo ); - _chdir( $rc{GL_REPO_BASE} ); + _chdir( "$rc{GL_REPO_BASE}/$repo.git" ); if ( $repo eq $last_repo ) { $repos{$repo} = $one_repo{$repo}; @@ -125,10 +125,10 @@ sub load_1 { return; } - if ( -f "$repo.git/gl-conf" ) { + if ( -f "gl-conf" ) { _die "split conf not set, gl-conf present for $repo" if not $split_conf{$repo}; - my $cc = "$repo.git/gl-conf"; + my $cc = "gl-conf"; _die "parse $cc failed: " . ( $! or $@ ) unless do $cc; $last_repo = $repo; From 17476318b9e0f4e2c9b515b1f63c74a0f37f1596 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Mar 2012 09:26:12 +0530 Subject: [PATCH 522/850] (trace) formatting changed when more than one arg passed --- src/Gitolite/Common.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Gitolite/Common.pm b/src/Gitolite/Common.pm index 6c62403..d2f2cea 100644 --- a/src/Gitolite/Common.pm +++ b/src/Gitolite/Common.pm @@ -39,10 +39,11 @@ sub say2 { sub trace { return unless defined( $ENV{D} ); - my $level = shift; + my $level = shift; return if $ENV{D} < $level; my $args = ''; $args = join( ", ", @_ ) if @_; my $sub = ( caller 1 )[3] || ''; $sub =~ s/.*://; $sub .= ' ' x ( 32 - length($sub) ); - say2 "TRACE $level $sub", $args if $ENV{D} >= $level; + say2 "TRACE $level $sub", (@_ ? shift : ()); + say2("TRACE $level " . (" " x 32), $_)for @_; } sub dbg { From fb69f6e3284253086bb4e00c4dff9aa841051ca6 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Mar 2012 13:10:32 +0530 Subject: [PATCH 523/850] (test setup) make Test.pm do a bit more --- src/Gitolite/Test.pm | 14 +++++++- t/basic.t | 76 +++++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/Gitolite/Test.pm b/src/Gitolite/Test.pm index 8251c41..0e15181 100644 --- a/src/Gitolite/Test.pm +++ b/src/Gitolite/Test.pm @@ -36,12 +36,24 @@ try " DEF AP_2 = AP_1; git add conf ; ok; git commit -m %1; ok; /master.* %1/ DEF ADMIN_PUSH = AP_2 %1; glt push admin origin; ok; gsh; /master -> master/ + DEF CS_1 = pwd; //tmp/tsh_tempdir.*gitolite-admin/; git remote -v; ok; /file://gitolite-admin/ + DEF CHECK_SETUP = CS_1; git log; ok; /65a1b2acd78dd9a7a401fe81c25380c1ca90067c/ + + DEF CLONE = glt clone + DEF PUSH = glt push + + # clean install mkdir -p $ENV{HOME}/bin ln -sf $ENV{PWD}/src/gitolite $ENV{PWD}/t/glt ~/bin cd; rm -vrf .gito* gito* repositories - cd tsh_tempdir; + # setup gitolite setup -a admin + + # clone admin repo + cd tsh_tempdir + glt clone admin --progress file://gitolite-admin + cd gitolite-admin " or die "could not setup the test environment; errors:\n\n" . text() . "\n\n"; sub dump { diff --git a/t/basic.t b/t/basic.t index 96c4012..8b1aa7c 100755 --- a/t/basic.t +++ b/t/basic.t @@ -10,14 +10,16 @@ use Gitolite::Test; # ---------------------------------------------------------------------- try " - plan 213 + plan 218 + CHECK_SETUP ## subtest 1 - glt clone dev2 file://gitolite-admin + cd .. + CLONE dev2 file://gitolite-admin ga2 !ok; gsh /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ - glt clone admin --progress file://gitolite-admin + CLONE admin --progress file://gitolite-admin ga2 ok; gsh /Counting/; /Compressing/; /Total/ cd gitolite-admin; ok @@ -39,12 +41,12 @@ try " git add conf; ok git status -s; ok; /M conf/gitolite.conf/ git commit -m t01a; ok; /master.*t01a/ - glt push dev2 origin; !ok; gsh + PUSH dev2 origin; !ok; gsh /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ - glt push admin origin; ok; /master -> master/ + PUSH admin origin; ok; /master -> master/ tsh empty; ok; - glt push admin origin master:mm + PUSH admin origin master:mm !ok; gsh /DENIED by refs/heads/mm/ reject @@ -70,31 +72,31 @@ try " # clone cd ..; ok; - glt clone u1 file://t1; !ok; gsh + CLONE u1 file://t1; !ok; gsh /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ - glt clone u2 file://t1; ok; gsh + CLONE u2 file://t1; ok; gsh /warning: You appear to have cloned an empty repository./ ls -al t1; ok; /$ENV{USER}.*$ENV{USER}.*\.git/ cd t1; ok; # push test-commit tc1 tc2 tc2; ok; /f7153e3/ - glt push u2 origin; !ok; gsh + PUSH u2 origin; !ok; gsh /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ - glt push u3 origin master; ok; gsh + PUSH u3 origin master; ok; gsh /master -> master/ # rewind reset-h HEAD^; ok; /HEAD is now at 537f964 tc2/ test-tick; test-commit tc3; ok; /a691552/ - glt push u3 origin; !ok; gsh + PUSH u3 origin; !ok; gsh /rejected.*master -> master.*non-fast-forward./ - glt push u3 -f origin; !ok; gsh + PUSH u3 -f origin; !ok; gsh reject /DENIED by fallthru/ - glt push u4 origin +master; ok; gsh + PUSH u4 origin +master; ok; gsh / \\+ f7153e3...a691552 master -> master.*forced update./ "; @@ -129,11 +131,11 @@ try " "; try " - glt clone u1 file://aa; ok; gsh + CLONE u1 file://aa; ok; gsh cd aa; ok test-commit set3 t1 t2 t3 t4 t5 t6 t7 t8 t9 ok - glt push u1 origin HEAD; ok; gsh + PUSH u1 origin HEAD; ok; gsh /To file://aa/ /\\* \\[new branch\\] HEAD -> master/ branch dev; ok @@ -142,52 +144,52 @@ try " # u1 rewind master ok reset-h HEAD^; ok test-commit r1; ok - glt push u1 origin +master; ok; gsh + PUSH u1 origin +master; ok; gsh /To file://aa/ /\\+ cecf671...70469f5 master -> master .forced update./ # u2 rewind master !ok reset-h HEAD^; ok test-commit r2; ok - glt push u2 origin +master; !ok; gsh + PUSH u2 origin +master; !ok; gsh reject /DENIED by fallthru/ # u3 rewind master ok reset-h HEAD^; ok test-commit r3; ok - glt push u3 origin +master; ok; gsh + PUSH u3 origin +master; ok; gsh /To file://aa/ /\\+ 70469f5...f1e6821 master -> master .forced update./ # u4 push master ok test-commit u4; ok - glt push u4 origin master; ok; gsh + PUSH u4 origin master; ok; gsh /To file://aa/ /f1e6821..d308cfb +master -> master/ # u4 rewind master !ok reset-h HEAD^; ok - glt push u4 origin +master; !ok; gsh + PUSH u4 origin +master; !ok; gsh reject /DENIED by fallthru/ # u3,u4 push other branches !ok - glt push u3 origin dev; !ok; gsh + PUSH u3 origin dev; !ok; gsh reject /DENIED by fallthru/ - glt push u4 origin dev; !ok; gsh + PUSH u4 origin dev; !ok; gsh reject /DENIED by fallthru/ - glt push u3 origin foo; !ok; gsh + PUSH u3 origin foo; !ok; gsh reject /DENIED by fallthru/ - glt push u4 origin foo; !ok; gsh + PUSH u4 origin foo; !ok; gsh reject /DENIED by fallthru/ # clean up for next set - glt push u1 -f origin master dev foo + PUSH u1 -f origin master dev foo ok; gsh /d308cfb...f1e6821 master -> master .forced update./ /new branch.*dev -> dev/ @@ -195,18 +197,18 @@ try " # u5 push master !ok test-commit u5 - glt push u5 origin master; !ok; gsh + PUSH u5 origin master; !ok; gsh reject /DENIED by refs/heads/master/ # u5 rewind dev ok - glt push u5 origin +dev^:dev + PUSH u5 origin +dev^:dev ok; gsh /\\+ cecf671...5c8a89d dev\\^ -> dev .forced update./ # u5 rewind foo !ok - glt push u5 origin +foo^:foo + PUSH u5 origin +foo^:foo !ok; gsh reject /remote: FATAL: \\+ refs/heads/foo aa u5 DENIED by fallthru/ @@ -216,15 +218,15 @@ try " /Switched to branch 'foo'/ test-commit u5 - glt push u5 origin foo; ok; gsh + PUSH u5 origin foo; ok; gsh /cecf671..b27cf19 *foo -> foo/ # u1 delete dev ok - glt push u1 origin :dev; ok; gsh + PUSH u1 origin :dev; ok; gsh / - \\[deleted\\] *dev/ # push it back - glt push u1 origin dev; ok; gsh + PUSH u1 origin dev; ok; gsh /\\* \\[new branch\\] *dev -> dev/ "; @@ -242,15 +244,15 @@ try " cd ..; ok - glt clone tester file://r1; ok; gsh + CLONE tester file://r1; ok; gsh /Cloning into 'r1'.../ cd r1; ok test-commit r1a r1b r1c r1d r1e r1f ok - glt push tester origin HEAD;ok; gsh + PUSH tester origin HEAD; ok; gsh /\\* \\[new branch\\] *HEAD -> master/ git branch v1 - glt push tester origin v1; ok; gsh + PUSH tester origin v1; ok; gsh /\\* \\[new branch\\] *v1 -> v1/ "; @@ -269,14 +271,14 @@ try " cd ..; ok - glt clone tester file://r2; ok; gsh + CLONE tester file://r2; ok; gsh /Cloning into 'r2'.../ cd r2; ok test-commit r2a r2b r2c r2d r2e r2f ok - glt push tester origin HEAD;ok; gsh + PUSH tester origin HEAD; ok; gsh /\\* \\[new branch\\] *HEAD -> master/ git branch v1 - glt push tester origin v1; !ok; gsh + PUSH tester origin v1; !ok; gsh /W refs/heads/v1 r2 tester DENIED by refs/heads/v\\[0-9\\]/ " From ef021ee2934b8b0d1233a93c521934115f0653eb Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Mar 2012 16:24:00 +0530 Subject: [PATCH 524/850] (test) forgot to set user.email/name to the standard value caused old test scripts to fail (wherever I was checking the actual SHA anyway) --- src/Gitolite/Test.pm | 4 +++- t/basic.t | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Gitolite/Test.pm b/src/Gitolite/Test.pm index 0e15181..f950fb3 100644 --- a/src/Gitolite/Test.pm +++ b/src/Gitolite/Test.pm @@ -37,7 +37,7 @@ try " DEF ADMIN_PUSH = AP_2 %1; glt push admin origin; ok; gsh; /master -> master/ DEF CS_1 = pwd; //tmp/tsh_tempdir.*gitolite-admin/; git remote -v; ok; /file://gitolite-admin/ - DEF CHECK_SETUP = CS_1; git log; ok; /65a1b2acd78dd9a7a401fe81c25380c1ca90067c/ + DEF CHECK_SETUP = CS_1; git log; ok; /6b18ec2ab0f765122ec133959b36c57f77d4565c/ DEF CLONE = glt clone DEF PUSH = glt push @@ -46,6 +46,8 @@ try " mkdir -p $ENV{HOME}/bin ln -sf $ENV{PWD}/src/gitolite $ENV{PWD}/t/glt ~/bin cd; rm -vrf .gito* gito* repositories + git config --global user.name \"gitolite tester\" + git config --global user.email \"tester\@example.com\" # setup gitolite setup -a admin diff --git a/t/basic.t b/t/basic.t index 8b1aa7c..fddc342 100755 --- a/t/basic.t +++ b/t/basic.t @@ -81,7 +81,7 @@ try " cd t1; ok; # push - test-commit tc1 tc2 tc2; ok; /f7153e3/ + test-commit tc1 tc2 tc2; ok; /a530e66/ PUSH u2 origin; !ok; gsh /DENIED by fallthru/ /fatal: The remote end hung up unexpectedly/ @@ -89,15 +89,15 @@ try " /master -> master/ # rewind - reset-h HEAD^; ok; /HEAD is now at 537f964 tc2/ - test-tick; test-commit tc3; ok; /a691552/ + reset-h HEAD^; ok; /HEAD is now at aa2b5c5 tc2/ + test-tick; test-commit tc3; ok; /3ffced1/ PUSH u3 origin; !ok; gsh /rejected.*master -> master.*non-fast-forward./ PUSH u3 -f origin; !ok; gsh reject /DENIED by fallthru/ PUSH u4 origin +master; ok; gsh - / \\+ f7153e3...a691552 master -> master.*forced update./ + / \\+ a530e66...3ffced1 master -> master.*forced update./ "; put "../gitolite-admin/conf/gitolite.conf", " @@ -146,7 +146,7 @@ try " test-commit r1; ok PUSH u1 origin +master; ok; gsh /To file://aa/ - /\\+ cecf671...70469f5 master -> master .forced update./ + /\\+ 27ed463...05adfb0 master -> master .forced update./ # u2 rewind master !ok reset-h HEAD^; ok @@ -160,13 +160,13 @@ try " test-commit r3; ok PUSH u3 origin +master; ok; gsh /To file://aa/ - /\\+ 70469f5...f1e6821 master -> master .forced update./ + /\\+ 05adfb0...6a532fe master -> master .forced update./ # u4 push master ok test-commit u4; ok PUSH u4 origin master; ok; gsh /To file://aa/ - /f1e6821..d308cfb +master -> master/ + /6a532fe..f929773 +master -> master/ # u4 rewind master !ok reset-h HEAD^; ok @@ -191,7 +191,7 @@ try " # clean up for next set PUSH u1 -f origin master dev foo ok; gsh - /d308cfb...f1e6821 master -> master .forced update./ + /f929773...6a532fe master -> master .forced update./ /new branch.*dev -> dev/ /new branch.*foo -> foo/ @@ -204,7 +204,7 @@ try " # u5 rewind dev ok PUSH u5 origin +dev^:dev ok; gsh - /\\+ cecf671...5c8a89d dev\\^ -> dev .forced update./ + /\\+ 27ed463...1ad477a dev\\^ -> dev .forced update./ # u5 rewind foo !ok @@ -219,7 +219,7 @@ try " test-commit u5 PUSH u5 origin foo; ok; gsh - /cecf671..b27cf19 *foo -> foo/ + /27ed463..83da62c *foo -> foo/ # u1 delete dev ok PUSH u1 origin :dev; ok; gsh From 16d17def2a9c4fc24fd8f5db1bf5c55e61759033 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Mar 2012 07:36:42 +0530 Subject: [PATCH 525/850] VREF code --- src/Gitolite/Conf/Load.pm | 50 ++++++++++++++++++++++++++---------- src/Gitolite/Conf/Store.pm | 4 +-- src/Gitolite/Conf/Sugar.pm | 18 ++++++++++++- src/Gitolite/Hooks/Update.pm | 41 +++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 17 deletions(-) diff --git a/src/Gitolite/Conf/Load.pm b/src/Gitolite/Conf/Load.pm index 171927e..625d7eb 100644 --- a/src/Gitolite/Conf/Load.pm +++ b/src/Gitolite/Conf/Load.pm @@ -6,6 +6,7 @@ package Gitolite::Conf::Load; @EXPORT = qw( load access + vrefs list_groups list_users @@ -139,26 +140,47 @@ sub load_1 { } } -sub rules { - my ( $repo, $user ) = @_; - trace( 4, "repo=$repo, user=$user" ); - my @rules = (); +{ + my $lastrepo = ''; + my $lastuser = ''; + my @cached = (); - my @repos = memberships($repo); - my @users = memberships($user); - trace( 4, "memberships: " . scalar(@repos) . " repos and " . scalar(@users) . " users found" ); + sub rules { + my ( $repo, $user ) = @_; + trace( 4, "repo=$repo, user=$user" ); - for my $r (@repos) { - for my $u (@users) { - push @rules, @{ $repos{$r}{$u} } if exists $repos{$r}{$u}; + return @cached if ($lastrepo eq $repo and $lastuser eq $user and @cached); + + my @rules = (); + + my @repos = memberships($repo); + my @users = memberships($user); + trace( 4, "memberships: " . scalar(@repos) . " repos and " . scalar(@users) . " users found" ); + + for my $r (@repos) { + for my $u (@users) { + push @rules, @{ $repos{$r}{$u} } if exists $repos{$r}{$u}; + } } + + @rules = sort { $a->[0] <=> $b->[0] } @rules; + + $lastrepo = $repo; + $lastuser = $user; + @cached = @rules; + + return @rules; } - # dbg("before sorting rules:", \@rules); - @rules = sort { $a->[0] <=> $b->[0] } @rules; - # dbg("after sorting rules:", \@rules); + sub vrefs { + my ( $repo, $user ) = @_; + # fill the cache if needed + rules($repo, $user) unless ($lastrepo eq $repo and $lastuser eq $user and @cached); - return @rules; + my %seen; + my @vrefs = grep { /^VREF\// and not $seen{$_}++ } map { $_->[2] } @cached; + return @vrefs; + } } sub memberships { diff --git a/src/Gitolite/Conf/Store.pm b/src/Gitolite/Conf/Store.pm index 37824c7..23d918f 100644 --- a/src/Gitolite/Conf/Store.pm +++ b/src/Gitolite/Conf/Store.pm @@ -95,9 +95,9 @@ sub parse_refs { # if no ref is given, this PERM applies to all refs @refs = qw(refs/.*) unless @refs; - # fully qualify refs that dont start with "refs/" or "NAME/" or "VREF/"; + # fully qualify refs that dont start with "refs/" or "VREF/"; # prefix them with "refs/heads/" - @refs = map { m(^(refs|NAME|VREF)/) or s(^)(refs/heads/); $_ } @refs; + @refs = map { m(^(refs|VREF)/) or s(^)(refs/heads/); $_ } @refs; # XXX what do we do? @refs = map { s(/USER/)(/\$gl_user/); $_ } @refs; return @refs; diff --git a/src/Gitolite/Conf/Sugar.pm b/src/Gitolite/Conf/Sugar.pm index dbf0926..30dcfc0 100644 --- a/src/Gitolite/Conf/Sugar.pm +++ b/src/Gitolite/Conf/Sugar.pm @@ -65,7 +65,7 @@ sub sugar { $lines = option($lines); $lines = owner_desc($lines); - # $lines = name_vref($lines); + $lines = name_vref($lines); return $lines; } @@ -132,5 +132,21 @@ sub owner_desc { return \@ret; } +sub name_vref { + my $lines = shift; + my @ret; + + # NAME/foo = + # -> VREF/NAME/foo = + + for my $line (@$lines) { + if ( $line =~ /^(-|R\S+) \S.* = \S.*/ ) { + $line =~ s( NAME/)( VREF/NAME/)g; + } + push @ret, $line; + } + return \@ret; +} + 1; diff --git a/src/Gitolite/Hooks/Update.pm b/src/Gitolite/Hooks/Update.pm index 488a3ec..dc94e97 100644 --- a/src/Gitolite/Hooks/Update.pm +++ b/src/Gitolite/Hooks/Update.pm @@ -28,9 +28,50 @@ sub update { trace( 1, "access($ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref) -> $ret" ); _die $ret if $ret =~ /DENIED/; + check_vrefs($ref, $oldsha, $newsha, $oldtree, $newtree, $aa); + exit 0; } +sub check_vrefs { + my($ref, $oldsha, $newsha, $oldtree, $newtree, $aa) = @_; + my $name_seen = 0; + for my $vref ( vrefs($ENV{GL_REPO}, $ENV{GL_USER}) ) { + trace(1, "vref=$vref"); + if ($vref =~ m(^VREF/NAME/)) { + # this one is special; we process it right here, and only once + next if $name_seen++; + + for my $ref ( map { chomp; s(^)(VREF/NAME/); $_; } `git diff --name-only $oldtree $newtree` ) { + check_vref($aa, $ref); + } + } else { + my($dummy, $pgm, @args) = split '/', $vref; + $pgm = "$ENV{GL_BINDIR}/VREF/$pgm"; + -x $pgm or die "$vref: helper program missing or unexecutable\n"; + + open( my $fh, "-|", $pgm, @_, $vref, @args ) or die "$vref: can't spawn helper program: $!\n"; + while (<$fh>) { + my ( $ref, $deny_message ) = split( ' ', $_, 2 ); + check_vref($aa, $ref, $deny_message); + } + close($fh) or die $! + ? "Error closing sort pipe: $!" + : "$vref: helper program exit status $?"; + } + } +} + +sub check_vref { + my ($aa, $ref, $deny_message) = @_; + + my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref ); + trace( 1, "access($ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref)", "-> $ret" ); + _die "$ret" . ( $deny_message ? "\n$deny_message" : '' ) + if $ret =~ /DENIED/ and $ret !~ /by fallthru/; + trace( 1, "remember, fallthru is success here!") if $ret =~ /by fallthru/; +} + { my $text = ''; From d64663d12e0421e6a806baf7502076697205fa72 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Mar 2012 16:38:46 +0530 Subject: [PATCH 526/850] COUNT VREF and tests --- src/VREF/COUNT | 51 ++++++++++++++++++ src/VREF/FILETYPE | 45 ++++++++++++++++ t/vrefs-1.t | 135 ++++++++++++++++++++++++++++++++++++++++++++++ t/vrefs-2.t | 106 ++++++++++++++++++++++++++++++++++++ 4 files changed, 337 insertions(+) create mode 100755 src/VREF/COUNT create mode 100755 src/VREF/FILETYPE create mode 100755 t/vrefs-1.t create mode 100755 t/vrefs-2.t diff --git a/src/VREF/COUNT b/src/VREF/COUNT new file mode 100755 index 0000000..f61ab57 --- /dev/null +++ b/src/VREF/COUNT @@ -0,0 +1,51 @@ +#!/bin/bash + +# gitolite VREF to count number of changed/new files in a push + +# see gitolite docs for what the first 7 arguments mean + +# inputs: +# arg-8 is a number +# arg-9 is optional, and can be "NEWFILES" +# outputs (STDOUT) +# arg-7 if the number of changed (or new, if arg-9 supplied) files is > arg-8 +# otherwise nothing +# exit status: +# always 0 + +die() { echo "$@" >&2; exit 1; } +[ -z "$8" ] && die "not meant to be run manually" + +newsha=$3 +oldtree=$4 +newtree=$5 +refex=$7 + +max=$8 + +nf= +[ "$9" = "NEWFILES" ] && nf='--diff-filter=A' +# NO_SIGNOFF implies NEWFILES +[ "$9" = "NO_SIGNOFF" ] && nf='--diff-filter=A' + +# count files against all the other commits in the system not just $oldsha +# (why? consider what is $oldtree when you create a new branch, or what is +# $oldsha when you update an old feature branch from master and then push it +count=`git log --name-only $nf --format=%n $newtree --not --all | grep . | sort -u | wc -l` + +[[ $count -gt $max ]] && { + # count has been exceeded. If $9 was NO_SIGNOFF there's still a chance + # for redemption -- if the top commit has a proper signed-off by line + [ "$9" = "NO_SIGNOFF" ] && { + author_email=$(git log --format=%ae -1 $newsha) + git cat-file -p $newsha | + egrep -i >/dev/null "^ *$count +new +files +signed-off by: *$author_email *$" && exit 0 + echo $refex top commit message should include the text \'$count new files signed-off by: $author_email\' + exit 0 + } + echo -n $refex "(too many " + [ -n "$nf" ] && echo -n "new " || echo -n "changed " + echo "files in this push)" +} + +exit 0 diff --git a/src/VREF/FILETYPE b/src/VREF/FILETYPE new file mode 100755 index 0000000..e61acc6 --- /dev/null +++ b/src/VREF/FILETYPE @@ -0,0 +1,45 @@ +#!/bin/bash + +# gitolite VREF to find autogenerated files + +# *completely* site specific; use it as an illustration of what can be done +# with gitolite VREFs if you wish + +# see gitolite docs for what the first 7 arguments mean + +# inputs: +# arg-8 is currently only one possible value: AUTOGENERATED +# outputs (STDOUT) +# arg-7 if any files changed in the push look like they were autogenerated +# otherwise nothing +# exit status: +# always 0 + +die() { echo "$@" >&2; exit 1; } +[ -z "$8" ] && die "not meant to be run manually" + +newsha=$3 +oldtree=$4 +newtree=$5 +refex=$7 + +option=$8 + +[ "$option" = "AUTOGENERATED" ] && { + # currently we only look for ".java" programs with the string "Generated + # by the protocol buffer compiler. DO NOT EDIT" in them. + + git log --name-only $nf --format=%n $newtree --not --all | + grep . | + sort -u | + grep '\.java$' | + while read fn + do + git show "$newtree:$fn" | egrep >/dev/null \ + 'Generated by the protocol buffer compiler. +DO NOT EDIT' || + continue + + echo $refex + exit 0 + done +} diff --git a/t/vrefs-1.t b/t/vrefs-1.t new file mode 100755 index 0000000..4512ab8 --- /dev/null +++ b/t/vrefs-1.t @@ -0,0 +1,135 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; + +try "plan 90"; + +put "conf/gitolite.conf", " + repo gitolite-admin + RW+ = admin + + \@gfoo = foo + \@lead = u1 + \@dev2 = u2 + \@dev4 = u4 + \@devs = \@dev2 \@dev4 u6 + repo \@gfoo + RW+ = \@lead \@devs + # intentional mis-spelling + - VREF/MISCOUNT/2 = \@dev2 + - VREF/MISCOUNT/4 = \@dev4 + - VREF/MISCOUNT/3/NEWFILES = u6 + - VREF/MISCOUNT/6 = u6 +"; + +try " + ADMIN_PUSH vr1a + cd .. + ls -al foo; !ok; /cannot access foo: No such file or directory/ + CLONE u1 file://foo; ok; /Cloning into/ + /You appear to have cloned an empty/ + cd foo; ok + ls -Al; ok; /\.git/ + + # VREF not called for u1 + tc a1 a2 a3 a4 a5; ok; /aaf9e8e/ + PUSH u1 origin master; ok; /new branch.*master -. master/ + !/helper program missing/ + !/hook declined/ + !/remote rejected/ + # VREF is called for u2 + tc b1; ok; /1f440d3/ + PUSH u2 origin; !ok; /helper program missing/ + /hook declined/ + /remote rejected/ +"; + +put "../gitolite-admin/conf/gitolite.conf", " + repo gitolite-admin + RW+ = admin + + \@gfoo = foo + \@lead = u1 + \@dev2 = u2 + \@dev4 = u4 + \@devs = \@dev2 \@dev4 u6 + repo \@gfoo + RW+ = \@lead \@devs + - VREF/COUNT/2 = \@dev2 + - VREF/COUNT/4 = \@dev4 + - VREF/COUNT/3/NEWFILES = u6 + - VREF/COUNT/6 = u6 +"; + +try " + ADMIN_PUSH vr1b + cd ../foo; ok + + # u2 1 file + PUSH u2 origin; ok; /aaf9e8e..1f440d3.*master -. master/ + + # u2 2 files + tc b2 b3; ok; /c3397f7/ + PUSH u2 origin; ok; /1f440d3..c3397f7.*master -. master/ + + # u2 3 files + tc c1 c2 c3; ok; /be242d7/ + PUSH u2 origin; !ok; /W VREF/COUNT/2 foo u2 DENIED by VREF/COUNT/2/ + /too many changed files in this push/ + /hook declined/ + /remote rejected/ + + # u4 3 files + PUSH u4 origin; ok; /c3397f7..be242d7.*master -. master/ + + # u4 4 files + tc d1 d2 d3 d4; ok; /88d80e2/ + PUSH u4 origin; ok; /be242d7..88d80e2.*master -. master/ + + # u4 5 files + tc d5 d6 d7 d8 d9; ok; /e9c60b0/ + PUSH u4 origin; !ok; /W VREF/COUNT/4 foo u4 DENIED by VREF/COUNT/4/ + /too many changed files in this push/ + /hook declined/ + /remote rejected/ + + # u1 all files + PUSH u1 origin; ok; /88d80e2..e9c60b0.*master -. master/ + + # u6 6 old files + test-tick + tc d1 d2 d3 d4 d5 d6 + ok; /2773f0a/ + PUSH u6 origin; ok; /e9c60b0..2773f0a.*master -. master/ + tag six + + # u6 updates 7 old files + test-tick; test-tick + tc d1 d2 d3 d4 d5 d6 d7 + ok; /d3fb574/ + PUSH u6 origin; !ok; /W VREF/COUNT/6 foo u6 DENIED by VREF/COUNT/6/ + /too many changed files in this push/ + /hook declined/ + /remote rejected/ + reset-h six; ok; /HEAD is now at 2773f0a/ + + # u6 4 new 2 old files + test-tick; test-tick + tc d1 d2 n1 n2 n3 n4 + ok; /9e90848/ + PUSH u6 origin; !ok; /W VREF/COUNT/3/NEWFILES foo u6 DENIED by VREF/COUNT/3/NEWFILES/ + /too many new files in this push/ + /hook declined/ + /remote rejected/ + reset-h six; ok; /HEAD is now at 2773f0a/ + + # u6 3 new 3 old files + test-tick; test-tick + tc d1 d2 d3 n1 n2 n3 + ok; /e47ff5d/ + PUSH u6 origin; ok; /2773f0a..e47ff5d.*master -. master/ +"; diff --git a/t/vrefs-2.t b/t/vrefs-2.t new file mode 100755 index 0000000..3ff1037 --- /dev/null +++ b/t/vrefs-2.t @@ -0,0 +1,106 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; + +try "plan 74"; + +put "../gitolite-admin/conf/gitolite.conf", " + \@gfoo = foo + \@lead = u1 + \@senior_devs = u2 u3 + \@junior_devs = u4 u5 u6 + repo \@gfoo + + RW+ = \@all + + RW+ VREF/COUNT/2/NO_SIGNOFF = \@lead + - VREF/COUNT/2/NO_SIGNOFF = \@all + + - VREF/COUNT/10/NEWFILES = \@junior_devs + + - VREF/FILETYPE/AUTOGENERATED = \@all +"; + +try " + ADMIN_PUSH vr2a + cd .. + # setup + ls -al foo; !ok; /cannot access foo: No such file or directory/ + CLONE u1 file://foo; ok; /Cloning into/ + /You appear to have cloned an empty/ + cd foo; ok + ls -Al; ok; /\.git/ + + # u1 push 15 new files + tc a b c d e f g h i j k l m n o + ok; /d8c0392/ + PUSH u1 origin master; ok; /new branch.*master -. master/ + + # u2 push 2 new 10 old without signoff + tc a b c d e f g h i j u2a u2b + ok; /6787ac9/ + PUSH u2 origin; ok; /d8c0392..6787ac9.*master -. master/ + + # u2 fail to push 3 new files without signoff + tc u2c u2d u2e; ok; /a74562b/ + PUSH u2 origin; !ok; /W VREF/COUNT/2/NO_SIGNOFF foo u2 DENIED by VREF/COUNT/2/NO_SIGNOFF/ + /top commit message should include the text .3 new files signed-off by: tester.example.com./ + /hook declined/ + /remote rejected/ + # u2 push 15 new files with signoff + tc u2f u2g u2h u2i u2j u2k u2l u2m u2n u2o u2p u2q + ok; /8dd31aa/ + git commit --allow-empty -m '15 new files signed-off by: tester\@example.com' + ok; /.master 6126489. 15 new files signed-off by: tester.example.com/ + PUSH u2 origin; ok; /6787ac9..6126489.*master -. master/ + + # u4 push 2 new 10 old files without signoff + tc u4a u4b a b c d e f g h i j + ok; /76c5593/ + PUSH u4 origin; ok; /6126489..76c5593.*master -. master/ + + # u4 fail push 3 new files withoug signoff + tc u4c u4d u4e; ok; /2a84398/ + PUSH u4 origin; !ok; /W VREF/COUNT/2/NO_SIGNOFF foo u4 DENIED by VREF/COUNT/2/NO_SIGNOFF/ + /top commit message should include the text .3 new files signed-off by: tester.example.com./ + /hook declined/ + /remote rejected/ + + # u4 push 10 new 5 old with signoff + tc u4f u4g u4h u4i u4j u4k u4l a b c d e + ok; /09b646a/ + git commit --allow-empty -m '10 new files signed-off by: tester\@example.com' + ok; /.master 47f84b0. 10 new files signed-off by: tester.example.com/ + PUSH u4 origin; ok; /76c5593..47f84b0.*master -. master/ + + # u4 fail push 11 new files even with signoff + tc u4ab u4ac u4ad u4ae u4af u4ag u4ah u4ai u4aj u4ak u4al + ok; /90e7344/ + git commit --allow-empty -m '11 new files signed-off by: tester\@example.com' + ok; /.master 1f36537. 11 new files signed-off by: tester.example.com/ + PUSH u4 origin; !ok; /W VREF/COUNT/10/NEWFILES foo u4 DENIED by VREF/COUNT/10/NEWFILES/ + /too many new files in this push/ + /hook declined/ + /remote rejected/ + + # test AUTOGENERATED vref + glt fetch u1 origin; ok; + reset-h origin/master; ok; + tc not-really.java; ok; /0f88b2e/ + PUSH u4 origin; ok; /47f84b0..0f88b2e.*master -. master/ +"; + +put "|cat >> not-really.java", " + Generated by the protocol buffer compiler. DO NOT EDIT +"; + +try " + commit -am pbc; ok; /b2df6ef/ + PUSH u4 origin; !ok; /W VREF/FILETYPE/AUTOGENERATED foo u4 DENIED by VREF/FILETYPE/AUTOGENERATED/ + /hook declined/ + /remote rejected/ +"; From c19f75e119df6577473be97d70a71732969f71a9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Mar 2012 18:30:06 +0530 Subject: [PATCH 527/850] (subconf) add the warning message (not as prominent as in g2 though...) --- src/Gitolite/Conf.pm | 1 + src/Gitolite/Conf/Store.pm | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/Gitolite/Conf.pm b/src/Gitolite/Conf.pm index 6c7dca6..6fcc0cf 100644 --- a/src/Gitolite/Conf.pm +++ b/src/Gitolite/Conf.pm @@ -73,6 +73,7 @@ sub parse { _warn "?? $line"; } } + parse_done(); } 1; diff --git a/src/Gitolite/Conf/Store.pm b/src/Gitolite/Conf/Store.pm index 23d918f..c513669 100644 --- a/src/Gitolite/Conf/Store.pm +++ b/src/Gitolite/Conf/Store.pm @@ -15,6 +15,7 @@ package Gitolite::Conf::Store; new_repo hook_repos store + parse_done ); use Exporter 'import'; @@ -205,6 +206,13 @@ sub store { store_common(); } +sub parse_done { + for my $ig (sort keys %ignored) + { + _warn "$ig.conf attempting to set access for " . join (", ", sort keys %{ $ignored{$ig} }); + } +} + # ---------------------------------------------------------------------- sub check_subconf_repo_disallowed { From ef476f0d32d85529d0bf6a15af5de7a7c0db1f06 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Mar 2012 20:46:31 +0530 Subject: [PATCH 528/850] common: slurp() learns to look at wantarray --- src/Gitolite/Common.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Gitolite/Common.pm b/src/Gitolite/Common.pm index d2f2cea..3ac2301 100644 --- a/src/Gitolite/Common.pm +++ b/src/Gitolite/Common.pm @@ -127,7 +127,8 @@ sub _print { } sub slurp { - local $/ = undef; + return unless defined wantarray; + local $/ = undef unless wantarray; my $fh = _open( "<", $_[0] ); return <$fh>; } From 428485086f676bb9a6948b799bb4b369e1ddec8d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Mar 2012 21:45:07 +0530 Subject: [PATCH 529/850] query-rc learned '-n' to avoid the need to chomp() the result --- src/Gitolite/Rc.pm | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Gitolite/Rc.pm b/src/Gitolite/Rc.pm index 7ed5cef..d716b6d 100644 --- a/src/Gitolite/Rc.pm +++ b/src/Gitolite/Rc.pm @@ -101,7 +101,10 @@ sub glrc { =for usage Usage: gitolite query-rc -a - gitolite query-rc + gitolite query-rc [-n] + + -a print all variables and values + -n do not append a newline Example: @@ -115,6 +118,7 @@ Example: # ---------------------------------------------------------------------- my $all = 0; +my $nonl = 0; sub query_rc { trace( 1, "rc file not found; default should be " . glrc('default-filename') ) if not glrc('filename'); @@ -123,14 +127,14 @@ sub query_rc { no strict 'refs'; - if ( $vars[0] eq '-a' ) { + if ( $all ) { for my $e (sort keys %rc) { print "$e=" . ( defined($rc{$e}) ? $rc{$e} : 'undef' ) . "\n"; } return; } - print join( "\t", map { $rc{$_} } @vars ) . "\n" if @vars; + print join( "\t", map { $rc{$_} || '' } @vars ) . ($nonl ? '' : "\n") if @vars; } # ---------------------------------------------------------------------- @@ -140,11 +144,11 @@ sub args { GetOptions( 'all|a' => \$all, + 'nonl|n' => \$nonl, 'help|h' => \$help, ) or usage(); usage("'-a' cannot be combined with other arguments") if $all and @ARGV; - return '-a' if $all; usage() if not $all and not @ARGV or $help; return @ARGV; } From 84422ccf3077930ce2294ccf9447b0a1b20b8e80 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Mar 2012 08:38:50 +0530 Subject: [PATCH 530/850] (rc) prefix GL_BINDIR to PATH Needed when the user didn't actually "install" but is just running it by using the full path to "gitolite". Without this, every time my code runs "gitolite " I have to prefix "gitolite" with $ENV{GL_BINDIR}, which is kinda painful... --- src/Gitolite/Rc.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Gitolite/Rc.pm b/src/Gitolite/Rc.pm index d716b6d..f526fde 100644 --- a/src/Gitolite/Rc.pm +++ b/src/Gitolite/Rc.pm @@ -52,6 +52,9 @@ _die "$rc seems to be for older gitolite" if defined($GL_ADMINDIR); # let values specified in rc file override our internal ones @rc{ keys %RC } = values %RC; +# fix PATH (TODO: do it only if 'gitolite' isn't in PATH) +$ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}"; + # ---------------------------------------------------------------------- use strict; From cbd4d4368783a2990718658cf3f1b19d1abb3f20 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Mar 2012 10:43:33 +0530 Subject: [PATCH 531/850] (minor) usage() sub can handle multiple usage sections in the same script --- src/Gitolite/Common.pm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Gitolite/Common.pm b/src/Gitolite/Common.pm index 3ac2301..eb4b6f1 100644 --- a/src/Gitolite/Common.pm +++ b/src/Gitolite/Common.pm @@ -75,10 +75,12 @@ sub _die { } sub usage { - _warn(shift) if @_; + my ($warn, $section) = @_; + _warn($warn) if $warn; + $section ||= 'usage'; my $scriptname = ( caller() )[1]; my $script = slurp($scriptname); - $script =~ /^=for usage(.*?)^=cut/sm; + $script =~ /^=for $section(.*?)^=cut/sm; say2( $1 ? $1 : "...no usage message in $scriptname" ); exit 1; } From 0aeb0cd5e2e37387a7fff5e11ff3012eb5b65646 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 11 Mar 2012 21:33:15 +0530 Subject: [PATCH 532/850] ssh-authkeys done! --- src/Gitolite/Hooks/PostUpdate.pm | 22 +++++- src/Gitolite/Hooks/Update.pm | 4 +- src/Gitolite/Rc.pm | 19 +++-- src/Gitolite/Setup.pm | 1 + src/gitolite | 32 +++++++- src/post-compile/ssh-authkeys | 129 +++++++++++++++++++++++++++++++ t/reset | 4 + t/ssh-authkeys.t | 73 +++++++++++++++++ 8 files changed, 274 insertions(+), 10 deletions(-) create mode 100755 src/post-compile/ssh-authkeys create mode 100755 t/ssh-authkeys.t diff --git a/src/Gitolite/Hooks/PostUpdate.pm b/src/Gitolite/Hooks/PostUpdate.pm index ef8e522..ab94e23 100644 --- a/src/Gitolite/Hooks/PostUpdate.pm +++ b/src/Gitolite/Hooks/PostUpdate.pm @@ -19,7 +19,7 @@ use warnings; # ---------------------------------------------------------------------- sub post_update { - trace(3); + trace(3, @ARGV); # this is the *real* post_update hook for gitolite tsh_try("git ls-tree --name-only master"); @@ -31,6 +31,24 @@ sub post_update { } _system("$ENV{GL_BINDIR}/gitolite compile"); + # now run optional post-compile features + if (exists $rc{POST_COMPILE}) { + if (ref($rc{POST_COMPILE}) ne 'ARRAY') { + _warn "bad syntax for specifying post compile scripts; see docs"; + } else { + for my $s (@{ $rc{POST_COMPILE} }) { + + # perl-ism; apart from keeping the full path separate from the + # simple name, this also protects %rc from change by implicit + # aliasing, which would happen if you touched $s itself + my $sfp = "$ENV{GL_BINDIR}/post-compile/$s"; + + _warn("skipped post-compile script '$s'"), next if not -x $sfp; + _system($sfp, @ARGV); # they better all return with 0 exit codes! + } + } + } + exit 0; } @@ -64,5 +82,5 @@ use Gitolite::Hooks::PostUpdate; # gitolite post-update hook (only for the admin repo) # ---------------------------------------------------------------------- -post_update(@ARGV); # is not expected to return +post_update(); # is not expected to return exit 1; # so if it does, something is wrong diff --git a/src/Gitolite/Hooks/Update.pm b/src/Gitolite/Hooks/Update.pm index dc94e97..cc13465 100644 --- a/src/Gitolite/Hooks/Update.pm +++ b/src/Gitolite/Hooks/Update.pm @@ -19,7 +19,7 @@ use warnings; # ---------------------------------------------------------------------- sub update { - trace( 3, @_ ); + trace( 3, @ARGV ); # this is the *real* update hook for gitolite my ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ) = args(@ARGV); @@ -150,5 +150,5 @@ use Gitolite::Hooks::Update; # gitolite update hook # ---------------------------------------------------------------------- -update(@ARGV); # is not expected to return +update(); # is not expected to return exit 1; # so if it does, something is wrong diff --git a/src/Gitolite/Rc.pm b/src/Gitolite/Rc.pm index f526fde..be82ab2 100644 --- a/src/Gitolite/Rc.pm +++ b/src/Gitolite/Rc.pm @@ -165,19 +165,28 @@ __DATA__ # PLEASE READ THE DOCUMENTATION BEFORE EDITING OR ASKING QUESTIONS -# this file is in perl syntax. However, you do NOT need to know perl to edit -# it; it should be fairly self-explanatory and easy to maintain. Just mind -# the commas, make sure the brackets and braces stay matched up! +# This file is in perl syntax. + +# However, you do NOT need to know perl to edit it; it should be fairly +# self-explanatory and easy to maintain. Just mind the commas (perl is quite +# happy to have an extra one at the end of the last item in any list, by the +# way!). And make sure the brackets and braces stay matched up! %RC = ( UMASK => 0077, GL_GITCONFIG_KEYS => "", - # uncomment as needed + # comment out or uncomment as needed SYNTACTIC_SUGAR => [ # 'continuation-lines', - ] + ], + + # comment out or uncomment as needed + POST_COMPILE => + [ + 'ssh-authkeys', + ], ); # ------------------------------------------------------------------------------ diff --git a/src/Gitolite/Setup.pm b/src/Gitolite/Setup.pm index b42e0fc..d335147 100644 --- a/src/Gitolite/Setup.pm +++ b/src/Gitolite/Setup.pm @@ -47,6 +47,7 @@ sub setup { } _system("$ENV{GL_BINDIR}/gitolite compile"); + _system("$ENV{GL_BINDIR}/gitolite post-compile ssh-authkeys") if $pubkey; hook_repos(); # all of them, just to be sure } diff --git a/src/gitolite b/src/gitolite index 0457cc2..d8c82af 100755 --- a/src/gitolite +++ b/src/gitolite @@ -6,7 +6,8 @@ =for usage Usage: gitolite [sub-command] [options] -The following subcommands are available; they should all respond to '-h': +The following subcommands are available; they should all respond to '-h' if +you want further details on each: setup 1st run: initial setup; all runs: hook fixups compile compile gitolite.conf @@ -17,6 +18,7 @@ The following subcommands are available; they should all respond to '-h': list-phy-repos list all repos actually on disk list-memberships list all groups a name is a member of list-members list all members of a group + post-compile run a post-compile command Warnings: - list-users is disk bound and could take a while on sites with 1000s of repos @@ -89,7 +91,35 @@ sub args { require Gitolite::Conf::Load; Gitolite::Conf::Load->import; print "$_\n" for ( @{ list_members() } ); + } elsif ( $command eq 'post-compile' ) { + shift @ARGV; + post_compile(); } else { _die "unknown gitolite sub-command"; } } + +=for post-compile +Usage: gitolite post-compile [-l] [post-compile-scriptname] [script args...] + + -l list currently available post-compile scripts + +Run a post-compile script (which normally runs from the post-update hook in +the gitolite-admin repo). +=cut + +sub post_compile { + usage('', 'post-compile') if (@ARGV and $ARGV[0] eq '-h'); + + if (@ARGV and $ARGV[0] eq '-l') { + _chdir("$ENV{GL_BINDIR}/post-compile"); + map { say2($_) } grep { -x } glob("*"); + exit 0; + } + + my $pgm = shift @ARGV; + my $fullpath = "$ENV{GL_BINDIR}/post-compile/$pgm"; + _die "$pgm not found or not executable" if not -x $fullpath; + _system($fullpath, @ARGV); + exit 0; +} diff --git a/src/post-compile/ssh-authkeys b/src/post-compile/ssh-authkeys new file mode 100755 index 0000000..c45e388 --- /dev/null +++ b/src/post-compile/ssh-authkeys @@ -0,0 +1,129 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use File::Temp qw(tempfile); + +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; + +$|++; + +# can be called directly, or as a post-update hook. Since it ignores +# arguments anyway, it hardly matters. + +my $ab = `gitolite query-rc -n GL_ADMIN_BASE`; +_warn("'keydir' not found in '$ab'"), exit if not -d "$ab/keydir"; +my $akfile = "$ENV{HOME}/.ssh/authorized_keys"; +my $glshell = `gitolite query-rc -n GL_BINDIR` . "/gitolite-shell"; +# XXX gl-time not yet coded (GL_PERFLOGT) +my $auth_options = auth_options(); + +sanity(); + +# ---------------------------------------------------------------------- + +_chdir($ab); + +# old data +my $old_ak = slurp($akfile); +my @non_gl = grep { not /^# gito.*start/ .. /^# gito.*end/ } slurp($akfile); +chomp(@non_gl); +my %seen = map { $_ => 'a non-gitolite key' } ( fp(@non_gl) ); +# die 1; + +# pubkey files +chomp( my @pubkeys = `find keydir -type f -name "*.pub" | sort` ); +my @gl_keys = (); +for my $f (@pubkeys) { + my $fp = fp($f); + if ( $seen{$fp} ) { + _warn "$f duplicates $seen{$fp}, sshd will ignore it"; + } else { + $seen{$fp} = $f; + } + push @gl_keys, grep { /./ } optionise($f); +} + +# dump it out +if (@gl_keys) { + my $out = join( "\n", map { my $_ = $_; chomp($_); $_ } @non_gl, "# gitolite start", @gl_keys, "# gitolite end" ) . "\n"; + + my $ak = slurp($akfile); + _die "$akfile changed between start and end of this program!" if $ak ne $old_ak; + _print( $akfile, $out ); +} + +# ---------------------------------------------------------------------- + +sub sanity { + _warn "$akfile missing; creating a new one" if not -f $akfile; + _die "$glshell not found; this should NOT happen..." if not -f $glshell; + _die "$glshell found but not readable; this should NOT happen..." if not -r $glshell; + _die "$glshell found but not executable; this should NOT happen..." if not -x $glshell; + + if ( not -f $akfile ) { + _print( $akfile, "" ); + chmod 0700, $akfile; + } +} + +sub auth_options { + my $auth_options = `gitolite query-rc AUTH_OPTIONS`; + chomp($auth_options); + $auth_options ||= "no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; + + return $auth_options; +} + +sub fp { + # input: see below + # output: a (list of) FPs + my $in = shift || ''; + if ( $in =~ /\.pub$/ ) { + # single pubkey file + return fp_file($in); + } elsif ( -f $in ) { + # an authkeys file + return map { fp_line($_) } grep { !/^#/ and /\S/ } slurp($in); + } else { + # one or more actual keys + return map { fp_line($_) } grep { !/^#/ and /\S/ } ($in, @_); + } +} + +sub fp_file { + my $f = shift; + my $fp = `ssh-keygen -l -f $f`; + chomp($fp); + _die "fingerprinting failed for $f" unless $fp =~ /([0-9a-f][0-9a-f](:[0-9a-f][0-9a-f])+)/; + $fp = $1; + return $fp; +} + +sub fp_line { + my ( $fh, $fn ) = tempfile(); + print $fh shift; + close $fh; + my $fp = fp_file($fn); + unlink $fn; + return $fp; +} + +sub optionise { + my $f = shift; + + my $user = $f; + $user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub + $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz + + my @line = slurp($f); + if ( @line != 1 ) { + _warn "$f does not contain exactly 1 line; ignoring"; + return ''; + } + chomp(@line); + return "command=\"$glshell $user\",$auth_options $line[0]"; +} + diff --git a/t/reset b/t/reset index 8c5dcbf..cfdc3db 100755 --- a/t/reset +++ b/t/reset @@ -2,6 +2,10 @@ use strict; use warnings; +BEGIN { + unlink "$ENV{HOME}/.ssh/authorized_keys"; +} + # this is hardcoded; change it if needed use lib "src"; use Gitolite::Test; diff --git a/t/ssh-authkeys.t b/t/ssh-authkeys.t new file mode 100755 index 0000000..64179b4 --- /dev/null +++ b/t/ssh-authkeys.t @@ -0,0 +1,73 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; + +$ENV{GL_BINDIR} = "$ENV{PWD}/src"; + +my $ak = "$ENV{HOME}/.ssh/authorized_keys"; +my $kd = `gitolite query-rc -n GL_ADMIN_BASE` . "/keydir"; + +try "plan 50"; + +my $pgm = "gitolite post-compile ssh-authkeys"; + +try " + # prep + rm -rf $ak; ok + + $pgm; ok; /'keydir' not found/ + mkdir $kd; ok + cd $kd; ok + $pgm; ok; /authorized_keys missing/ + /creating/ + wc < $ak; ok; /0 *0 *0/ + # some gl keys + ssh-keygen -N '' -q -f alice -C alice + ssh-keygen -N '' -q -f bob -C bob + ssh-keygen -N '' -q -f carol -C carol + ssh-keygen -N '' -q -f dave -C dave + ssh-keygen -N '' -q -f eve -C eve + rm alice bob carol dave eve + ls -a; ok; /alice.pub/; /bob.pub/; /carol.pub/; /dave.pub/; /eve.pub/ + $pgm; ok; + wc < $ak; ok; /^ *7 .*/; + grep gitolite $ak; ok; /start/ + /end/ + + # some normal keys + mv alice.pub $ak; ok + cat carol.pub >> $ak; ok + $pgm; ok; /carol.pub duplicates.*non-gitolite key/ + wc < $ak; ok; /^ *8 .*/; + + # moving normal keys up + mv dave.pub dave + $pgm; ok + cat dave >> $ak; ok + grep -n dave $ak; ok; /8:ssh-rsa/ + mv dave dave.pub + $pgm; ok; /carol.pub duplicates.*non-gitolite key/ + /dave.pub duplicates.*non-gitolite key/ + grep -n dave $ak; ok; /3:ssh-rsa/ + + # a bad key + ls -al > bad.pub + $pgm; !ok; /fingerprinting failed for keydir/bad.pub/ + wc < $ak; ok; /^ *9 .*/; + # a good key doesn't get added + ssh-keygen -N '' -q -f good + $pgm; !ok; /fingerprinting failed for keydir/bad.pub/ + wc < $ak; ok; /^ *9 .*/; + # till the bad key is removed + rm bad.pub + $pgm; ok; + wc < $ak; ok; /^ *10 .*/; + + # duplicate gl key + cp bob.pub robert.pub + $pgm; ok; /robert.pub duplicates.*bob.pub/ +"; From 7f8020adc5709690e74f0d07838022e43533c272 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 12 Mar 2012 20:54:30 +0530 Subject: [PATCH 533/850] 'info' command, plus lots more changes: - usage() gets a little smarter; it now knows what function it was called from and tries to find a '=for function_name' chunk of data in the script - the various list-* functions now work off a dispatcher in Load.pm - (...and they all use the new usage() magic to print their helps!) - src/gitolite got a lot leaner due to this dispatcher - src/gitolite-shell became a lot more easier to read/flow - rc acquired '{COMMANDS}', which gitolite-shell now refers to - comments in the default rc file changed a bit - rc got a new REMOTE_COMMAND_PATT (in place of ADC_CMD_ARGS_PATT) the rest is perltidy and stuff like that --- src/Gitolite/Common.pm | 33 ++++--- src/Gitolite/Conf.pm | 6 +- src/Gitolite/Conf/Explode.pm | 2 +- src/Gitolite/Conf/Load.pm | 86 +++++++++-------- src/Gitolite/Conf/Store.pm | 5 +- src/Gitolite/Conf/Sugar.pm | 12 +-- src/Gitolite/Hooks/PostUpdate.pm | 10 +- src/Gitolite/Hooks/Update.pm | 22 ++--- src/Gitolite/Rc.pm | 72 ++++++++------ src/Gitolite/Setup.pm | 5 +- src/Gitolite/Test.pm | 4 +- src/Gitolite/Test/Tsh.pm | 3 +- src/VREF/COUNT | 1 + src/VREF/FILETYPE | 1 + src/commands/info | 31 ++++++ src/gitolite | 129 +++++++++++++------------ src/gitolite-shell | 93 ++++++++++++++---- src/post-compile/ssh-authkeys | 2 +- src/syntactic-sugar/continuation-lines | 4 +- t/glt | 5 +- 20 files changed, 317 insertions(+), 209 deletions(-) create mode 100755 src/commands/info diff --git a/src/Gitolite/Common.pm b/src/Gitolite/Common.pm index eb4b6f1..2260e41 100644 --- a/src/Gitolite/Common.pm +++ b/src/Gitolite/Common.pm @@ -40,10 +40,10 @@ sub trace { return unless defined( $ENV{D} ); my $level = shift; return if $ENV{D} < $level; - my $args = ''; $args = join( ", ", @_ ) if @_; - my $sub = ( caller 1 )[3] || ''; $sub =~ s/.*://; $sub .= ' ' x ( 32 - length($sub) ); - say2 "TRACE $level $sub", (@_ ? shift : ()); - say2("TRACE $level " . (" " x 32), $_)for @_; + my $args = ''; $args = join( ", ", @_ ) if @_; + my $sub = ( caller 1 )[3] || ''; $sub =~ s/.*://; $sub .= ' ' x ( 32 - length($sub) ); + say2 "TRACE $level $sub", ( @_ ? shift : () ); + say2( "TRACE $level " . ( " " x 32 ), $_ ) for @_; } sub dbg { @@ -75,13 +75,17 @@ sub _die { } sub usage { - my ($warn, $section) = @_; - _warn($warn) if $warn; - $section ||= 'usage'; - my $scriptname = ( caller() )[1]; - my $script = slurp($scriptname); - $script =~ /^=for $section(.*?)^=cut/sm; - say2( $1 ? $1 : "...no usage message in $scriptname" ); + _warn(shift) if @_; + my ( $script, $function ) = ( caller(1) )[ 1, 3 ]; + if (not $script) { + $script = ( caller ) [1]; + $function = 'usage'; + } + dbg( "u s a g e", $script, $function ); + $function =~ s/.*:://; + my $code = slurp($script); + $code =~ /^=for $function(.*?)^=cut/sm; + say2( $1 ? $1 : "...no usage message in $script" ); exit 1; } @@ -154,8 +158,8 @@ sub ln_sf { sub sort_u { my %uniq; my $listref = shift; - return [] unless @{ $listref }; - undef @uniq{ @{ $listref } }; # expect a listref + return [] unless @{$listref}; + undef @uniq{ @{$listref} }; # expect a listref my @sort_u = sort keys %uniq; return \@sort_u; } @@ -177,7 +181,6 @@ sub cleanup_conf_line { my @phy_repos = (); sub list_phy_repos { - _die "'gitolite list_phy_repos' takes no arguments" if @ARGV; trace(3); # use cached value only if it exists *and* no arg was received (i.e., @@ -189,7 +192,7 @@ sub cleanup_conf_line { $repo =~ s(\./(.*)\.git$)($1); push @phy_repos, $repo; } - return sort_u(\@phy_repos); + return sort_u( \@phy_repos ); } } diff --git a/src/Gitolite/Conf.pm b/src/Gitolite/Conf.pm index 6fcc0cf..a93aa10 100644 --- a/src/Gitolite/Conf.pm +++ b/src/Gitolite/Conf.pm @@ -24,12 +24,12 @@ use warnings; sub compile { trace(3); - # XXX assume we're in admin-base/conf + _die "'gitolite compile' does not take any arguments" if @_; _chdir( $rc{GL_ADMIN_BASE} ); _chdir("conf"); - parse(sugar('gitolite.conf')); + parse( sugar('gitolite.conf') ); # the order matters; new repos should be created first, to give store a # place to put the individual gl-conf files @@ -39,7 +39,7 @@ sub compile { sub parse { my $lines = shift; - trace(4, scalar(@$lines) . " lines incoming"); + trace( 4, scalar(@$lines) . " lines incoming" ); for my $line (@$lines) { # user or repo groups diff --git a/src/Gitolite/Conf/Explode.pm b/src/Gitolite/Conf/Explode.pm index a821dc9..f77e89d 100644 --- a/src/Gitolite/Conf/Explode.pm +++ b/src/Gitolite/Conf/Explode.pm @@ -28,7 +28,7 @@ sub explode { # seed the 'seen' list if it's empty $included{ device_inode("conf/gitolite.conf") }++ unless %included; - my $fh = _open( "<", $file ); + my $fh = _open( "<", $file ); while (<$fh>) { my $line = cleanup_conf_line($_); next unless $line =~ /\S/; diff --git a/src/Gitolite/Conf/Load.pm b/src/Gitolite/Conf/Load.pm index 625d7eb..1759214 100644 --- a/src/Gitolite/Conf/Load.pm +++ b/src/Gitolite/Conf/Load.pm @@ -7,12 +7,7 @@ package Gitolite::Conf::Load; load access vrefs - - list_groups - list_users - list_repos - list_memberships - list_members + lister_dispatch ); use Exporter 'import'; @@ -25,8 +20,6 @@ use warnings; # ---------------------------------------------------------------------- -my $subconf = 'master'; - # our variables, because they get loaded by a 'do' our $data_version = ''; our %repos; @@ -36,6 +29,16 @@ our %configs; our %one_config; our %split_conf; +my $subconf = 'master'; + +my %listers = ( + 'list-groups' => \&list_groups, + 'list-users' => \&list_users, + 'list-repos' => \&list_repos, + 'list-memberships' => \&list_memberships, + 'list-members' => \&list_members, +); + # helps maintain the "cache" in both "load_common" and "load_1" my $last_repo = ''; @@ -118,7 +121,7 @@ sub load_1 { my $repo = shift; trace( 4, $repo ); - _chdir( "$rc{GL_REPO_BASE}/$repo.git" ); + _chdir("$rc{GL_REPO_BASE}/$repo.git"); if ( $repo eq $last_repo ) { $repos{$repo} = $one_repo{$repo}; @@ -143,13 +146,13 @@ sub load_1 { { my $lastrepo = ''; my $lastuser = ''; - my @cached = (); + my @cached = (); sub rules { my ( $repo, $user ) = @_; trace( 4, "repo=$repo, user=$user" ); - return @cached if ($lastrepo eq $repo and $lastuser eq $user and @cached); + return @cached if ( $lastrepo eq $repo and $lastuser eq $user and @cached ); my @rules = (); @@ -167,7 +170,7 @@ sub load_1 { $lastrepo = $repo; $lastuser = $user; - @cached = @rules; + @cached = @rules; return @rules; } @@ -175,7 +178,7 @@ sub load_1 { sub vrefs { my ( $repo, $user ) = @_; # fill the cache if needed - rules($repo, $user) unless ($lastrepo eq $repo and $lastuser eq $user and @cached); + rules( $repo, $user ) unless ( $lastrepo eq $repo and $lastuser eq $user and @cached ); my %seen; my @vrefs = grep { /^VREF\// and not $seen{$_}++ } map { $_->[2] } @cached; @@ -200,15 +203,22 @@ sub data_version_mismatch { # api functions # ---------------------------------------------------------------------- -# list all groups -sub list_groups { - die " +sub lister_dispatch { + my $command = shift; + + my $fn = $listers{$command} or _die "unknown gitolite sub-command"; + return $fn; +} + +=for list_groups Usage: gitolite list-groups - lists all group names in conf - no options, no flags +=cut -" if @ARGV; +sub list_groups { + usage() if @_; load_common(); @@ -219,18 +229,18 @@ Usage: gitolite list-groups return ( sort_u( \@g ) ); } -sub list_users { - my $count = 0; - my $total = 0; - - die " +=for list_users Usage: gitolite list-users - lists all users/user groups in conf - no options, no flags - WARNING: may be slow if you have thousands of repos +=cut -" if @ARGV; +sub list_users { + usage() if @_; + my $count = 0; + my $total = 0; load_common(); @@ -242,19 +252,19 @@ Usage: gitolite list-users $count++; print STDERR "$count / $total\r" if not( $count % 100 ) and timer(5); push @u, map { keys %{$_} } values %one_repo; } - print STDERR "\n"; + print STDERR "\n" if $count >= 100; return ( sort_u( \@u ) ); } -sub list_repos { - - die " +=for list_repos Usage: gitolite list-repos - lists all repos/repo groups in conf - no options, no flags +=cut -" if @ARGV; +sub list_repos { + usage() if @_; load_common(); @@ -264,34 +274,34 @@ Usage: gitolite list-repos return ( sort_u( \@r ) ); } -sub list_memberships { - - die " +=for list_memberships Usage: gitolite list-memberships - list all groups a name is a member of - takes one user/repo name +=cut -" if @ARGV and $ARGV[0] eq '-h' or not @ARGV and not @_; +sub list_memberships { + usage() if @_ and $_[0] eq '-h' or not @_; - my $name = ( @_ ? shift @_ : shift @ARGV ); + my $name = shift; load_common(); my @m = memberships($name); return ( sort_u( \@m ) ); } -sub list_members { - - die " +=for list_members Usage: gitolite list-members - list all members of a group - takes one group name +=cut -" if @ARGV and $ARGV[0] eq '-h' or not @ARGV and not @_; +sub list_members { + usage() if @_ and $_[0] eq '-h' or not @_; - my $name = ( @_ ? shift @_ : shift @ARGV ); + my $name = shift; load_common(); diff --git a/src/Gitolite/Conf/Store.pm b/src/Gitolite/Conf/Store.pm index c513669..154b44e 100644 --- a/src/Gitolite/Conf/Store.pm +++ b/src/Gitolite/Conf/Store.pm @@ -207,9 +207,8 @@ sub store { } sub parse_done { - for my $ig (sort keys %ignored) - { - _warn "$ig.conf attempting to set access for " . join (", ", sort keys %{ $ignored{$ig} }); + for my $ig ( sort keys %ignored ) { + _warn "$ig.conf attempting to set access for " . join( ", ", sort keys %{ $ignored{$ig} } ); } } diff --git a/src/Gitolite/Conf/Sugar.pm b/src/Gitolite/Conf/Sugar.pm index 30dcfc0..caea1fb 100644 --- a/src/Gitolite/Conf/Sugar.pm +++ b/src/Gitolite/Conf/Sugar.pm @@ -3,7 +3,7 @@ package SugarBox; sub run_sugar_script { - my ($ss, $lref) = @_; + my ( $ss, $lref ) = @_; do $ss if -x $ss; $lref = sugar_script($lref); return $lref; @@ -35,7 +35,7 @@ sub sugar { # gets a filename, returns a listref my @lines = (); - explode(shift, 'master', \@lines); + explode( shift, 'master', \@lines ); my $lines; $lines = \@lines; @@ -43,11 +43,11 @@ sub sugar { # run through the sugar stack one by one # first, user supplied sugar: - if (exists $rc{SYNTACTIC_SUGAR}) { - if (ref($rc{SYNTACTIC_SUGAR}) ne 'ARRAY') { + if ( exists $rc{SYNTACTIC_SUGAR} ) { + if ( ref( $rc{SYNTACTIC_SUGAR} ) ne 'ARRAY' ) { _warn "bad syntax for specifying sugar scripts; see docs"; } else { - for my $s (@{ $rc{SYNTACTIC_SUGAR} }) { + for my $s ( @{ $rc{SYNTACTIC_SUGAR} } ) { # perl-ism; apart from keeping the full path separate from the # simple name, this also protects %rc from change by implicit @@ -55,7 +55,7 @@ sub sugar { my $sfp = "$ENV{GL_BINDIR}/syntactic-sugar/$s"; _warn("skipped sugar script '$s'"), next if not -x $sfp; - $lines = SugarBox::run_sugar_script($sfp, $lines); + $lines = SugarBox::run_sugar_script( $sfp, $lines ); $lines = [ grep /\S/, map { cleanup_conf_line($_) } @$lines ]; } } diff --git a/src/Gitolite/Hooks/PostUpdate.pm b/src/Gitolite/Hooks/PostUpdate.pm index ab94e23..efd4838 100644 --- a/src/Gitolite/Hooks/PostUpdate.pm +++ b/src/Gitolite/Hooks/PostUpdate.pm @@ -19,7 +19,7 @@ use warnings; # ---------------------------------------------------------------------- sub post_update { - trace(3, @ARGV); + trace( 3, @ARGV ); # this is the *real* post_update hook for gitolite tsh_try("git ls-tree --name-only master"); @@ -32,11 +32,11 @@ sub post_update { _system("$ENV{GL_BINDIR}/gitolite compile"); # now run optional post-compile features - if (exists $rc{POST_COMPILE}) { - if (ref($rc{POST_COMPILE}) ne 'ARRAY') { + if ( exists $rc{POST_COMPILE} ) { + if ( ref( $rc{POST_COMPILE} ) ne 'ARRAY' ) { _warn "bad syntax for specifying post compile scripts; see docs"; } else { - for my $s (@{ $rc{POST_COMPILE} }) { + for my $s ( @{ $rc{POST_COMPILE} } ) { # perl-ism; apart from keeping the full path separate from the # simple name, this also protects %rc from change by implicit @@ -44,7 +44,7 @@ sub post_update { my $sfp = "$ENV{GL_BINDIR}/post-compile/$s"; _warn("skipped post-compile script '$s'"), next if not -x $sfp; - _system($sfp, @ARGV); # they better all return with 0 exit codes! + _system( $sfp, @ARGV ); # they better all return with 0 exit codes! } } } diff --git a/src/Gitolite/Hooks/Update.pm b/src/Gitolite/Hooks/Update.pm index cc13465..da089b5 100644 --- a/src/Gitolite/Hooks/Update.pm +++ b/src/Gitolite/Hooks/Update.pm @@ -28,32 +28,32 @@ sub update { trace( 1, "access($ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref) -> $ret" ); _die $ret if $ret =~ /DENIED/; - check_vrefs($ref, $oldsha, $newsha, $oldtree, $newtree, $aa); + check_vrefs( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ); exit 0; } sub check_vrefs { - my($ref, $oldsha, $newsha, $oldtree, $newtree, $aa) = @_; + my ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ) = @_; my $name_seen = 0; - for my $vref ( vrefs($ENV{GL_REPO}, $ENV{GL_USER}) ) { - trace(1, "vref=$vref"); - if ($vref =~ m(^VREF/NAME/)) { + for my $vref ( vrefs( $ENV{GL_REPO}, $ENV{GL_USER} ) ) { + trace( 1, "vref=$vref" ); + if ( $vref =~ m(^VREF/NAME/) ) { # this one is special; we process it right here, and only once next if $name_seen++; for my $ref ( map { chomp; s(^)(VREF/NAME/); $_; } `git diff --name-only $oldtree $newtree` ) { - check_vref($aa, $ref); + check_vref( $aa, $ref ); } } else { - my($dummy, $pgm, @args) = split '/', $vref; + my ( $dummy, $pgm, @args ) = split '/', $vref; $pgm = "$ENV{GL_BINDIR}/VREF/$pgm"; -x $pgm or die "$vref: helper program missing or unexecutable\n"; open( my $fh, "-|", $pgm, @_, $vref, @args ) or die "$vref: can't spawn helper program: $!\n"; while (<$fh>) { my ( $ref, $deny_message ) = split( ' ', $_, 2 ); - check_vref($aa, $ref, $deny_message); + check_vref( $aa, $ref, $deny_message ); } close($fh) or die $! ? "Error closing sort pipe: $!" @@ -63,13 +63,13 @@ sub check_vrefs { } sub check_vref { - my ($aa, $ref, $deny_message) = @_; + my ( $aa, $ref, $deny_message ) = @_; my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref ); trace( 1, "access($ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref)", "-> $ret" ); _die "$ret" . ( $deny_message ? "\n$deny_message" : '' ) - if $ret =~ /DENIED/ and $ret !~ /by fallthru/; - trace( 1, "remember, fallthru is success here!") if $ret =~ /by fallthru/; + if $ret =~ /DENIED/ and $ret !~ /by fallthru/; + trace( 1, "remember, fallthru is success here!" ) if $ret =~ /by fallthru/; } { diff --git a/src/Gitolite/Rc.pm b/src/Gitolite/Rc.pm index be82ab2..2a51a55 100644 --- a/src/Gitolite/Rc.pm +++ b/src/Gitolite/Rc.pm @@ -8,7 +8,7 @@ package Gitolite::Rc; glrc query_rc - $ADC_CMD_ARGS_PATT + $REMOTE_COMMAND_PATT $REF_OR_FILENAME_PATT $REPONAME_PATT $REPOPATT_PATT @@ -36,7 +36,7 @@ $rc{GL_REPO_BASE} = "$ENV{HOME}/repositories"; # variables that should probably never be changed # ---------------------------------------------------------------------- -$ADC_CMD_ARGS_PATT = qr(^[0-9a-zA-Z._\@/+:-]*$); +$REMOTE_COMMAND_PATT = qr(^[- 0-9a-zA-Z\@\%_=+:,./]*$); $REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][0-9a-zA-Z._\@/+ :,-]*$); $REPONAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$); $REPOPATT_PATT = qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/,-]*$); @@ -101,26 +101,9 @@ sub glrc { # implements 'gitolite query-rc' # ---------------------------------------------------------------------- -=for usage - -Usage: gitolite query-rc -a - gitolite query-rc [-n] - - -a print all variables and values - -n do not append a newline - -Example: - - gitolite query-rc GL_ADMIN_BASE GL_UMASK - # prints "/home/git/.gitolite0077" or similar - - gitolite query-rc -a - # prints all known variables and values, one per line -=cut - # ---------------------------------------------------------------------- -my $all = 0; +my $all = 0; my $nonl = 0; sub query_rc { @@ -130,18 +113,38 @@ sub query_rc { no strict 'refs'; - if ( $all ) { - for my $e (sort keys %rc) { - print "$e=" . ( defined($rc{$e}) ? $rc{$e} : 'undef' ) . "\n"; + if ($all) { + for my $e ( sort keys %rc ) { + print "$e=" . ( defined( $rc{$e} ) ? $rc{$e} : 'undef' ) . "\n"; } - return; + exit 0; } - print join( "\t", map { $rc{$_} || '' } @vars ) . ($nonl ? '' : "\n") if @vars; + my @res = map { $rc{$_} } grep { $rc{$_} } @vars; + print join( "\t", @res ) . ( $nonl ? '' : "\n" ) if @res; + # shell truth + exit 0 if @res; + exit 1; } # ---------------------------------------------------------------------- +=for args +Usage: gitolite query-rc -a + gitolite query-rc [-n] + + -a print all variables and values + -n do not append a newline + +Example: + + gitolite query-rc GL_ADMIN_BASE UMASK + # prints "/home/git/.gitolite0077" or similar + + gitolite query-rc -a + # prints all known variables and values, one per line +=cut + sub args { my $help = 0; @@ -163,30 +166,35 @@ sub args { __DATA__ # configuration variables for gitolite -# PLEASE READ THE DOCUMENTATION BEFORE EDITING OR ASKING QUESTIONS +# This file is in perl syntax. But you do NOT need to know perl to edit it -- +# just mind the commas and make sure the brackets and braces stay matched up! -# This file is in perl syntax. - -# However, you do NOT need to know perl to edit it; it should be fairly -# self-explanatory and easy to maintain. Just mind the commas (perl is quite -# happy to have an extra one at the end of the last item in any list, by the -# way!). And make sure the brackets and braces stay matched up! +# (Tip: perl allows a comma after the last item in a list also!) %RC = ( UMASK => 0077, GL_GITCONFIG_KEYS => "", # comment out or uncomment as needed + # these will run in sequence during the conf file parse SYNTACTIC_SUGAR => [ # 'continuation-lines', ], # comment out or uncomment as needed + # these will run in sequence after post-update POST_COMPILE => [ 'ssh-authkeys', ], + + # comment out or uncomment as needed + # these are available to remote users + COMMANDS => + { + 'info' => 1, + }, ); # ------------------------------------------------------------------------------ diff --git a/src/Gitolite/Setup.pm b/src/Gitolite/Setup.pm index d335147..09930bd 100644 --- a/src/Gitolite/Setup.pm +++ b/src/Gitolite/Setup.pm @@ -3,14 +3,15 @@ package Gitolite::Setup; # implements 'gitolite setup' # ---------------------------------------------------------------------- -=for usage +=for args Usage: gitolite setup [] - -a, --admin admin user name -pk --pubkey pubkey file name -f, --fixup-hooks fixup hooks +Setup (first run only), then compile conf and fixup hooks. + First run: -a required -pk required for ssh mode install diff --git a/src/Gitolite/Test.pm b/src/Gitolite/Test.pm index f950fb3..f7b4544 100644 --- a/src/Gitolite/Test.pm +++ b/src/Gitolite/Test.pm @@ -17,8 +17,8 @@ use Carp qw(carp cluck croak confess); BEGIN { require Gitolite::Test::Tsh; - *{'try'} = \&Tsh::try; - *{'put'} = \&Tsh::put; + *{'try'} = \&Tsh::try; + *{'put'} = \&Tsh::put; *{'text'} = \&Tsh::text; } diff --git a/src/Gitolite/Test/Tsh.pm b/src/Gitolite/Test/Tsh.pm index b4b3b41..41b4d12 100644 --- a/src/Gitolite/Test/Tsh.pm +++ b/src/Gitolite/Test/Tsh.pm @@ -259,8 +259,7 @@ sub rc_lines { $cmd = shift @cmds; # is the current command a "testing" command? - my $testing_cmd = - ( $cmd =~ m(^ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^!ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^/(.*?)/(?:\s+or\s+(.*))?$) or $cmd =~ m(^!/(.*?)/(?:\s+or\s+(.*))?$) ); + my $testing_cmd = ( $cmd =~ m(^ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^!ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^/(.*?)/(?:\s+or\s+(.*))?$) or $cmd =~ m(^!/(.*?)/(?:\s+or\s+(.*))?$) ); # warn if the previous command failed but rc is not being checked if ( $rc and not $testing_cmd ) { diff --git a/src/VREF/COUNT b/src/VREF/COUNT index f61ab57..d5c3982 100755 --- a/src/VREF/COUNT +++ b/src/VREF/COUNT @@ -1,4 +1,5 @@ #!/bin/bash +# TODO: convert to perl! # gitolite VREF to count number of changed/new files in a push diff --git a/src/VREF/FILETYPE b/src/VREF/FILETYPE index e61acc6..2115a5c 100755 --- a/src/VREF/FILETYPE +++ b/src/VREF/FILETYPE @@ -1,4 +1,5 @@ #!/bin/bash +# TODO: convert to perl! # gitolite VREF to find autogenerated files diff --git a/src/commands/info b/src/commands/info new file mode 100755 index 0000000..fe52837 --- /dev/null +++ b/src/commands/info @@ -0,0 +1,31 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; +use Gitolite::Conf::Load; + +=for usage +Usage: gitolite info + + - list all repos/repo groups you can access + - no options, no flags +=cut + +usage() if @ARGV; + +my $user = $ENV{GL_USER} or _die "GL_USER not set"; +my $ref = 'any'; + +my $fn = lister_dispatch('list-repos'); + +for ( @{ $fn->() } ) { + my $perm = ''; + for my $aa (qw(R W ^C)) { + my $ret = access($_, $user, $aa, $ref); + $perm .= ( $ret =~ /DENIED/ ? " " : " $aa" ); + } + print "$perm\t$_\n" if $perm =~ /\S/; +} diff --git a/src/gitolite b/src/gitolite index d8c82af..c265bd5 100755 --- a/src/gitolite +++ b/src/gitolite @@ -3,7 +3,7 @@ # all gitolite CLI tools run as sub-commands of this command # ---------------------------------------------------------------------- -=for usage +=for args Usage: gitolite [sub-command] [options] The following subcommands are available; they should all respond to '-h' if @@ -11,14 +11,16 @@ you want further details on each: setup 1st run: initial setup; all runs: hook fixups compile compile gitolite.conf + query-rc get values of rc variables + post-compile run a post-compile command + list-groups list all group names in conf list-users list all users/user groups in conf list-repos list all repos/repo groups in conf list-phy-repos list all repos actually on disk list-memberships list all groups a name is a member of list-members list all members of a group - post-compile run a post-compile command Warnings: - list-users is disk bound and could take a while on sites with 1000s of repos @@ -40,66 +42,56 @@ use warnings; # ---------------------------------------------------------------------- +my ( $command, @args ) = @ARGV; args(); +# the first two commands need options via @ARGV, as they have their own +# GetOptions calls and older perls don't have 'GetOptionsFromArray' + +if ( $command eq 'setup' ) { + shift @ARGV; + require Gitolite::Setup; + Gitolite::Setup->import; + setup(); + +} elsif ( $command eq 'query-rc' ) { + shift @ARGV; + query_rc(); # doesn't return + +# the rest don't need @ARGV per se + +} elsif ( $command eq 'compile' ) { + require Gitolite::Conf; + Gitolite::Conf->import; + compile(@args); + +} elsif ( $command eq 'post-compile' ) { + post_compile(@args); + +} elsif ( -x "$rc{GL_BINDIR}/commands/$command" ) { + run_command( $command, @args ); + +} elsif ( $command eq 'list-phy-repos' ) { + _chdir( $rc{GL_REPO_BASE} ); + print "$_\n" for ( @{ list_phy_repos(@args) } ); + +} elsif ( $command =~ /^list-/ ) { + require Gitolite::Conf::Load; + Gitolite::Conf::Load->import; + my $fn = lister_dispatch($command); + print "$_\n" for ( @{ $fn->(@args) } ); + +} else { + _die "unknown gitolite sub-command"; +} + +sub args { + usage() if not $command or $command eq '-h'; +} + # ---------------------------------------------------------------------- -sub args { - my ( $command, @args ) = @ARGV; - usage() if not $command or $command eq '-h'; - - if ( $command eq 'setup' ) { - shift @ARGV; - require Gitolite::Setup; - Gitolite::Setup->import; - setup(); - } elsif ( $command eq 'compile' ) { - shift @ARGV; - _die "'gitolite compile' does not take any arguments" if @ARGV; - require Gitolite::Conf; - Gitolite::Conf->import; - compile(); - } elsif ( $command eq 'query-rc' ) { - shift @ARGV; - query_rc(); - } elsif ( $command eq 'list-groups' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_groups() } ); - } elsif ( $command eq 'list-users' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_users() } ); - } elsif ( $command eq 'list-repos' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_repos() } ); - } elsif ( $command eq 'list-phy-repos' ) { - shift @ARGV; - _chdir( $rc{GL_REPO_BASE} ); - print "$_\n" for ( @{ list_phy_repos() } ); - } elsif ( $command eq 'list-memberships' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_memberships() } ); - } elsif ( $command eq 'list-members' ) { - shift @ARGV; - require Gitolite::Conf::Load; - Gitolite::Conf::Load->import; - print "$_\n" for ( @{ list_members() } ); - } elsif ( $command eq 'post-compile' ) { - shift @ARGV; - post_compile(); - } else { - _die "unknown gitolite sub-command"; - } -} - -=for post-compile +=for post_compile Usage: gitolite post-compile [-l] [post-compile-scriptname] [script args...] -l list currently available post-compile scripts @@ -109,17 +101,26 @@ the gitolite-admin repo). =cut sub post_compile { - usage('', 'post-compile') if (@ARGV and $ARGV[0] eq '-h'); + usage() if ( not @_ or $_[0] eq '-h' ); - if (@ARGV and $ARGV[0] eq '-l') { - _chdir("$ENV{GL_BINDIR}/post-compile"); + run_subdir('post-compile', @_); +} + +sub run_command { + run_subdir('commands', @_); +} + +sub run_subdir { + my $subdir = shift; + if ( @_ and $_[0] eq '-l' ) { + _chdir("$ENV{GL_BINDIR}/$subdir"); map { say2($_) } grep { -x } glob("*"); exit 0; } - my $pgm = shift @ARGV; - my $fullpath = "$ENV{GL_BINDIR}/post-compile/$pgm"; + my $pgm = shift; + my $fullpath = "$ENV{GL_BINDIR}/$subdir/$pgm"; _die "$pgm not found or not executable" if not -x $fullpath; - _system($fullpath, @ARGV); + _system( $fullpath, @_ ); exit 0; } diff --git a/src/gitolite-shell b/src/gitolite-shell index d7f6a19..c291bc9 100755 --- a/src/gitolite-shell +++ b/src/gitolite-shell @@ -13,38 +13,89 @@ use Gitolite::Conf::Load; use strict; use warnings; -print STDERR "TRACE: gsh(", join( ")(", @ARGV ), ")\n"; -print STDERR "TRACE: gsh(SOC=$ENV{SSH_ORIGINAL_COMMAND})\n"; + +# the main() sub expects ssh-ish things; set them up... +if ( exists $ENV{G3T_USER} ) { + in_local(); # file:// masquerading as ssh:// for easy testing +} elsif ( exists $ENV{SSH_CONNECTION} ) { + in_ssh(); +} elsif ( exists $ENV{REQUEST_URI} ) { + in_http(); +} else { + _die "who the *heck* are you?"; +} + +main(); + +exit 0; # ---------------------------------------------------------------------- # XXX lots of stuff from gl-auth-command is missing for now... -# set up the user -my $user = $ENV{GL_USER} = shift; +sub in_local { + print STDERR "TRACE: gsh(", join( ")(", @ARGV ), ")\n"; + print STDERR "TRACE: gsh(SOC=$ENV{SSH_ORIGINAL_COMMAND})\n"; +} -# set up the repo and the attempted access -my ( $verb, $repo ) = split_soc(); -sanity($repo); -$ENV{GL_REPO} = $repo; -my $aa = ( $verb =~ 'upload' ? 'R' : 'W' ); +sub in_http { + _die 'http not yet implemented...'; +} -# a ref of 'any' signifies that this is a pre-git check, where we don't -# yet know the ref that will be eventually pushed (and even that won't apply -# if it's a read operation). See the matching code in access() for more. -my $ret = access( $repo, $user, $aa, 'any' ); -trace( 1, "access($repo, $user, $aa, 'any') -> $ret" ); -_die $ret if $ret =~ /DENIED/; - -$repo = "'$rc{GL_REPO_BASE}/$repo.git'"; -exec( "git", "shell", "-c", "$verb $repo" ); +sub in_ssh { +} # ---------------------------------------------------------------------- -sub split_soc { +# call this once you are sure arg-1 is the username and SSH_ORIGINAL_COMMAND +# has been setup (even if it's not actually coming via ssh). +sub main { + # set up the user + my $user = $ENV{GL_USER} = shift @ARGV; + + # set up the repo and the attempted access + my ( $verb, $repo ) = parse_soc(); # returns only for git commands + sanity($repo); + $ENV{GL_REPO} = $repo; + my $aa = ( $verb =~ 'upload' ? 'R' : 'W' ); + + # a ref of 'any' signifies that this is a pre-git check, where we don't + # yet know the ref that will be eventually pushed (and even that won't + # apply if it's a read operation). See the matching code in access() for + # more information. + my $ret = access( $repo, $user, $aa, 'any' ); + trace( 1, "access($repo, $user, $aa, 'any') -> $ret" ); + _die $ret if $ret =~ /DENIED/; + + $repo = "'$rc{GL_REPO_BASE}/$repo.git'"; + exec( "git", "shell", "-c", "$verb $repo" ); +} + +# ---------------------------------------------------------------------- + +sub parse_soc { my $soc = $ENV{SSH_ORIGINAL_COMMAND}; - return ( $1, $2 ) if $soc =~ m(^(git-(?:upload|receive)-pack) '/?(.*?)(?:\.git)?'$); - _die "unknown command: $soc"; + $soc ||= 'info'; + + if ( $soc =~ m(^(git-(?:upload|receive)-pack) '/?(.*?)(?:\.git)?'$) ) { + # TODO git archive + my($verb, $repo) = ($1, $2); + _die "invalid repo name: '$repo'" if $repo !~ $REPONAME_PATT; + return ($verb, $repo); + } + + # after this we should not return; caller expects us to handle it all here + # and exit out + + _die "suspicious characters loitering about '$soc'" if $soc !~ $REMOTE_COMMAND_PATT; + + my @words = split ' ', $soc; + if ($rc{COMMANDS}{$words[0]}) { + _system("gitolite", @words); + exit 0; + } + + _die "unknown git/gitolite command: $soc"; } sub sanity { diff --git a/src/post-compile/ssh-authkeys b/src/post-compile/ssh-authkeys index c45e388..5e5ad4b 100755 --- a/src/post-compile/ssh-authkeys +++ b/src/post-compile/ssh-authkeys @@ -89,7 +89,7 @@ sub fp { return map { fp_line($_) } grep { !/^#/ and /\S/ } slurp($in); } else { # one or more actual keys - return map { fp_line($_) } grep { !/^#/ and /\S/ } ($in, @_); + return map { fp_line($_) } grep { !/^#/ and /\S/ } ( $in, @_ ); } } diff --git a/src/syntactic-sugar/continuation-lines b/src/syntactic-sugar/continuation-lines index 1d25379..3c28f20 100755 --- a/src/syntactic-sugar/continuation-lines +++ b/src/syntactic-sugar/continuation-lines @@ -18,10 +18,10 @@ sub sugar_script { my $lines = shift; - my @out = (); + my @out = (); my $keep = ''; for my $l (@$lines) { - if ($l =~ s/\\$//) { + if ( $l =~ s/\\$// ) { $keep .= $l; } else { $l = $keep . $l if $keep; diff --git a/t/glt b/t/glt index 45e7b19..09d4429 100755 --- a/t/glt +++ b/t/glt @@ -12,7 +12,10 @@ my $user = shift or die "need user"; my $rc; $ENV{G3T_USER} = $user; -if ( $cmd eq 'push' ) { +if ($cmd eq 'info' ) { + $ENV{SSH_ORIGINAL_COMMAND} = $cmd; + exec( "$ENV{GL_BINDIR}/../src/gitolite-shell", $user ); +} elsif ( $cmd eq 'push' ) { $rc = system( "git", $cmd, "--receive-pack=$ENV{GL_BINDIR}/gitolite-receive-pack", @ARGV ); } else { $rc = system( "git", $cmd, "--upload-pack=$ENV{GL_BINDIR}/gitolite-upload-pack", @ARGV ); From 356ff2b7572672f315f273116d60c556589331fd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 14 Mar 2012 14:11:43 +0530 Subject: [PATCH 534/850] store got a few more validations (a full scan of all input data is pending; this is just for diagnostics) --- src/Gitolite/Conf/Store.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Gitolite/Conf/Store.pm b/src/Gitolite/Conf/Store.pm index 154b44e..fef1746 100644 --- a/src/Gitolite/Conf/Store.pm +++ b/src/Gitolite/Conf/Store.pm @@ -49,6 +49,7 @@ my %ignored; sub add_to_group { my ( $lhs, @rhs ) = @_; _die "bad group '$lhs'" unless $lhs =~ $REPONAME_PATT; + map { _die "bad expansion '$_'" unless $_ =~ $REPOPATT_PATT } @rhs; # store the group association, but overload it to keep track of when # the group was *first* created by using $subconf as the *value* @@ -115,6 +116,8 @@ sub parse_users { sub add_rule { my ( $perm, $ref, $user ) = @_; + _die "bad ref '$ref'" unless $ref =~ $REPOPATT_PATT; + _die "bad user '$user'" unless $user =~ $USERNAME_PATT; $ruleseq++; for my $repo (@repolist) { @@ -140,6 +143,7 @@ sub add_rule { sub set_subconf { $subconf = shift; + _die "bad subconf '$subconf'" unless $subconf =~ /^[-\w.]+$/; trace( 1, $subconf ); } From 89cc3a303d1b71beb5075fed55d9ffae053b8954 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 14 Mar 2012 15:29:44 +0530 Subject: [PATCH 535/850] Test.pm learned confreset() and confadd() --- src/Gitolite/Test.pm | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Gitolite/Test.pm b/src/Gitolite/Test.pm index f7b4544..e78fe88 100644 --- a/src/Gitolite/Test.pm +++ b/src/Gitolite/Test.pm @@ -9,12 +9,16 @@ package Gitolite::Test; put text dump + confreset + confadd ); #>>> use Exporter 'import'; use File::Path qw(mkpath); use Carp qw(carp cluck croak confess); +use Gitolite::Common; + BEGIN { require Gitolite::Test::Tsh; *{'try'} = \&Tsh::try; @@ -65,4 +69,25 @@ sub dump { } } +sub _confargs { + return @_ if ($_[1]); + return 'gitolite.conf', $_[0]; +} + +sub confreset { + system("rm", "-rf", "conf"); + mkdir("conf"); + put "conf/gitolite.conf", ' + repo gitolite-admin + RW+ = admin + repo testing + RW+ = @all +'; +} + +sub confadd { + my ($file, $string) = _confargs(@_); + put "|cat >> conf/$file", $string; +} + 1; From 141b2ce8970cab434e562b4017ca82beb7438bc5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 14 Mar 2012 15:30:05 +0530 Subject: [PATCH 536/850] more tests --- src/Gitolite/Test.pm | 5 +++ t/include-subconf.t | 79 +++++++++++++++++++++++++++++++++ t/info.t | 52 ++++++++++++++++++++++ t/m-explode.t | 102 ------------------------------------------- 4 files changed, 136 insertions(+), 102 deletions(-) create mode 100755 t/include-subconf.t create mode 100755 t/info.t delete mode 100755 t/m-explode.t diff --git a/src/Gitolite/Test.pm b/src/Gitolite/Test.pm index e78fe88..df4cc89 100644 --- a/src/Gitolite/Test.pm +++ b/src/Gitolite/Test.pm @@ -77,6 +77,11 @@ sub _confargs { sub confreset { system("rm", "-rf", "conf"); mkdir("conf"); + system("mv ~/repositories/gitolite-admin.git ~/repositories/.ga"); + system("mv ~/repositories/testing.git ~/repositories/.te"); + system("find ~/repositories -name '*.git' |xargs rm -rf"); + system("mv ~/repositories/.ga ~/repositories/gitolite-admin.git"); + system("mv ~/repositories/.te ~/repositories/testing.git "); put "conf/gitolite.conf", ' repo gitolite-admin RW+ = admin diff --git a/t/include-subconf.t b/t/include-subconf.t new file mode 100755 index 0000000..7bb5b04 --- /dev/null +++ b/t/include-subconf.t @@ -0,0 +1,79 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; + +try 'plan 37'; + +confreset; confadd ' + include "i1.conf" + @i2 = b1 + subconf "i2.conf" + include "i1.conf" +'; +confadd 'i1.conf', ' + @g1 = a1 a2 + repo foo + RW = u1 + + include "j1.conf" +'; +confadd 'i2.conf', ' + @g2 = b1 b2 + repo bar b1 b2 i1 i2 @i1 @i2 @g2 + RW = u2 +'; +confadd 'j1.conf', ' + @h2 = c1 c2 + repo baz + RW = u3 +'; + +try "ADMIN_PUSH set2; !/FATAL/" or die text(); + +try " + /i1.conf already included/ + /i2.conf attempting to set access for \@i1, b2, bar, i1, locally modified \@g2/ + !/attempting to set access.*i2/ + /Initialized.*empty.*baz.git/ + /Initialized.*empty.*foo.git/ + /Initialized.*empty.*b1.git/ + /Initialized.*empty.*i2.git/ + !/Initialized.*empty.*b2.git/ + !/Initialized.*empty.*i1.git/ + !/Initialized.*empty.*bar.git/ +"; + +confreset;confadd ' + @g2 = i1 i2 i3 + subconf "g2.conf" +'; +confadd 'g2.conf', ' + @g2 = g2 h2 i2 + repo @g2 + RW = u1 +'; + +try "ADMIN_PUSH set3; !/FATAL/" or die text(); +try " + /g2.conf attempting to set access for locally modified \@g2/ + !/Initialized.*empty/ +"; + +confreset;confadd ' + @g2 = i1 i2 i3 + subconf "g2.conf" +'; +confadd 'g2.conf', ' + subconf master + @g2 = g2 h2 i2 + repo @g2 + RW = u1 +'; + +try " + ADMIN_PUSH set3; ok; /FATAL: subconf g2 attempting to run 'subconf'/ +"; diff --git a/t/info.t b/t/info.t new file mode 100755 index 0000000..967f4af --- /dev/null +++ b/t/info.t @@ -0,0 +1,52 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; + +try 'plan 35'; + +try "## info"; + +confreset;confadd ' + repo t1 + RW = u1 + R = u2 + repo t2 + RW = u2 + R = u1 + repo t3 + RW = u3 + R = u4 +'; + +try "ADMIN_PUSH info; !/FATAL/" or die text(); +try " + /Initialized.*empty.*t1.git/ + /Initialized.*empty.*t2.git/ + /Initialized.*empty.*t3.git/ +"; +try " + glt info u1; ok; gsh + /R W *\tt1/ + /R *\tt2/ + !/t3/ + / R W *\ttesting/ + glt info u2; ok; gsh + /R *\tt1/ + /R W *\tt2/ + !/t3/ + / R W *\ttesting/ + glt info u3; ok; gsh + /R W *\tt3/ + !/t1/ + !/t2/ + / R W *\ttesting/ + glt info u4; ok; gsh + /R *\tt3/ + !/t1/ + !/t2/ + / R W *\ttesting/ + " or die; diff --git a/t/m-explode.t b/t/m-explode.t deleted file mode 100755 index aef337f..0000000 --- a/t/m-explode.t +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/perl -use strict; -use warnings; -use 5.10.0; - -use Test; -BEGIN { plan tests => - 2 -} - -use lib "$ENV{PWD}/src"; -use Gitolite::Test; -use Gitolite::Conf::Explode; - -my @out; -my @out2; - -warn " - <<< expect a couple of warnings about already included files >>> -"; - -# test 1 -- space normalisation - - put "foo", " - foo line 1 - foo=line 2 - - - foo 3 - "; - @out = (); - explode("foo", 'master', \@out); - @out2 = ( - 'foo line 1', - 'foo = line 2', - 'foo 3', - ); - - ok(@out ~~ @out2); - -# test 2 -- include/subconf processing - - put "foo", " - foo line 1 - \@fog=line 2 - include \"bar.conf\" - - foo line=5 - subconf \"subs/baz.conf\" - include \"bar.conf\" - foo line=7 - include \"bazup.conf\" - "; - - put "bar.conf", " - \@brg=line 1 - - bar line 3 - "; - - mkdir("subs"); - - put "subs/baz.conf", " - \@bzg = line 1 - - include \"subs/baz2.conf\" - - baz=line 3 - "; - - put "subs/baz2.conf", " - baz2 line 1 - baz2 line 2 - include \"bazup.conf\" - baz2 line 4 - "; - - put "bazup.conf", " - whatever... - "; - - @out = (); - explode("foo", 'master', \@out); - - @out2 = ( - 'foo line 1', - '@fog = line 2', - '@brg = line 1', - 'bar line 3', - 'foo line = 5', - 'subconf baz', - '@baz.bzg = line 1', - 'baz2 line 1', - 'baz2 line 2', - 'whatever...', - 'baz2 line 4', - 'baz = line 3', - 'subconf master', - 'foo line = 7' - ); - - ok(@out ~~ @out2); From 4e25a8acd130cc39c2d5eed777feb66b8bb1235f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 05:07:37 +0530 Subject: [PATCH 537/850] ssh-basic tests (and that's all we will ever do; see below) ssh tests are meant to ensure that basic authENTICATION is happening. AuthORISATION is checked all over the rest of the test suite and these two are quite orthogonal operations so there is no need to test all of authZ with ssh. --- t/keys/admin | 27 +++++++++++++++++++++++++ t/keys/admin.pub | 1 + t/keys/config | 20 +++++++++++++++++++ t/keys/u1 | 27 +++++++++++++++++++++++++ t/keys/u1.pub | 1 + t/keys/u2 | 27 +++++++++++++++++++++++++ t/keys/u2.pub | 1 + t/keys/u3 | 27 +++++++++++++++++++++++++ t/keys/u3.pub | 1 + t/keys/u4 | 27 +++++++++++++++++++++++++ t/keys/u4.pub | 1 + t/keys/u5 | 27 +++++++++++++++++++++++++ t/keys/u5.pub | 1 + t/keys/u6 | 27 +++++++++++++++++++++++++ t/keys/u6.pub | 1 + t/ssh-basic.t | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 16 files changed, 267 insertions(+) create mode 100644 t/keys/admin create mode 100644 t/keys/admin.pub create mode 100644 t/keys/config create mode 100644 t/keys/u1 create mode 100644 t/keys/u1.pub create mode 100644 t/keys/u2 create mode 100644 t/keys/u2.pub create mode 100644 t/keys/u3 create mode 100644 t/keys/u3.pub create mode 100644 t/keys/u4 create mode 100644 t/keys/u4.pub create mode 100644 t/keys/u5 create mode 100644 t/keys/u5.pub create mode 100644 t/keys/u6 create mode 100644 t/keys/u6.pub create mode 100755 t/ssh-basic.t diff --git a/t/keys/admin b/t/keys/admin new file mode 100644 index 0000000..676a711 --- /dev/null +++ b/t/keys/admin @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA0/X7uwd7xOvC3UTZaAnFOR5xqhdgcyc8vk3d1bXXthiuUSmq +5t4uhS9qj0ismcPX0YRNhSDElotG1KSWp8DceJpR2c7GYmELpqoE7ebVPnEBKY0c +PX+G9KZNgbJyyx35QlpJmlO+LimM0oO8XarRphn3kc0jCTLKaI5ndEjGDHH4fsOe +wlryW7a0RR/brMNiJYYzy5ADCOkAnyXQmbrQW4nROCPqFojBFTEBLpe8EuODlJXE +mRHpa1k+k/grFgp/c1xbOrjox127LZT4vkLf5+lSwkhq8oyzWHUrQ+rUJZssi1LC +GEZSudhX5reqksmIlTwX0GPIIwSwZFNVxhj51wIDAQABAoIBAFFLlzE0vZPZmPOk +5H2ywaIWuyGxtZx1ACc9VkgRZprA/JrEkHfb35vVg9lQ1mJjavNA+zqERuI2qQQF +3IKaxfS7u4j+dbhl4EIcE6frUP6R+RAmvx4XO3u6DSAhgUXGSUPZvUEjvV2XMhvL +ywNh8Ob0LrANLdLpWBiiBavj/ZHnsVZj7y+39GKEtLm8BbsH8L7GanPl4uQDNruR +Ef2L2pSEqml+Iva6yk5a22U293JCVZ/aqDdGvrnbjkaxqin5jBgL47D91A3xo4SW +zFZkJR5SDYnFLdwnwTPvxSA9IXj6TZ2ZdnTNvI5Utpd0Wy5N4GlfXS3TBPBD4kP+ ++pS1vgECgYEA/D7k1IyOM2hGamHc0qG+sIxMHge6rJE8pYtW2suB2KX0bJvAeBY8 +eHM+pPFMbjjZHlU7qB9qmGbZ++ZVlEv/SqqHizvwLzth0P4MJ6/UjAcmAdEUclV0 +YSzMRFk/g0J7aMlJb5NXgA5xlArHLsmV3Zc+QwFPECgzz5LHwD5nTMcCgYEA1x2Y +qb7FXwcQPDcs4Qwux12tCUaExQMZbNoLHWrXw7ktHRfR9sI8TcEErLzc2wnZbdyZ +av/GYSTR9UCeq12kUhmTL4SNryktHKNKEfDEvR7wK6sa9iffzZH8FDe5JktLXnu7 +Oe5SNfLDHA5X7qQefz8s2658+xVJmdp+uasNOnECgYEAlzBXVbJ9VQCuG/tWMQVz +VzxwLxuw3tgagprWz0NlK2ak7ygXn6KsUgG5TYG3ruTx9gVeQXG7IWecRiiTqNQ4 +SxeVMHYXiyfLhEmRHYR9IAT02efomnLv04LXWCwqLlF9yJvFIVQuAPonR3WCV1/K +LMwHLIAvVF7UVxkCEw8UOWcCgYEAwiUH/0sZvuYVFQOHEaV5Ip286b4nXdeqPr+b +gHVJPnAF81foO5iZ7GLj4TKi8V02SxzpqdQmKs6cX4huq6LcBuzmFeDALvIusMX+ +t6phJX6irAbFUpwyNMoog+a2x4T1BNUO6P3aXK44wT2AxvSAQb+2sJ4OVl2kC6NS +9CcYzUECgYEAzqbWlJIbjJx5620Q95bu+lpessOUtoVcYryO6dQNZOiis8aDZjJk +RKdB9qhJlcUeFqnvJS5L5gohaJoyx0wMSYVB7MrO6kAIMHo/OriEQ6CTsQOTqa8n +q4cV9Wuk9pRb1b/x5eXCcHe8P7wBt8KKIh/VBB4aDyeIwGbO0NODYr4= +-----END RSA PRIVATE KEY----- diff --git a/t/keys/admin.pub b/t/keys/admin.pub new file mode 100644 index 0000000..b50a5b9 --- /dev/null +++ b/t/keys/admin.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDT9fu7B3vE68LdRNloCcU5HnGqF2BzJzy+Td3Vtde2GK5RKarm3i6FL2qPSKyZw9fRhE2FIMSWi0bUpJanwNx4mlHZzsZiYQumqgTt5tU+cQEpjRw9f4b0pk2BsnLLHflCWkmaU74uKYzSg7xdqtGmGfeRzSMJMspojmd0SMYMcfh+w57CWvJbtrRFH9usw2IlhjPLkAMI6QCfJdCZutBbidE4I+oWiMEVMQEul7wS44OUlcSZEelrWT6T+CsWCn9zXFs6uOjHXbstlPi+Qt/n6VLCSGryjLNYdStD6tQlmyyLUsIYRlK52Ffmt6qSyYiVPBfQY8gjBLBkU1XGGPnX g3@sita-lt.atc.tcs.com diff --git a/t/keys/config b/t/keys/config new file mode 100644 index 0000000..fc75580 --- /dev/null +++ b/t/keys/config @@ -0,0 +1,20 @@ +host * + stricthostkeychecking no +host admin + identityfile ~/.ssh/admin + +host u? admin + user %USER + hostname localhost +host u1 + identityfile ~/.ssh/u1 +host u2 + identityfile ~/.ssh/u2 +host u3 + identityfile ~/.ssh/u3 +host u4 + identityfile ~/.ssh/u4 +host u5 + identityfile ~/.ssh/u5 +host u6 + identityfile ~/.ssh/u6 diff --git a/t/keys/u1 b/t/keys/u1 new file mode 100644 index 0000000..828d1c3 --- /dev/null +++ b/t/keys/u1 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAuB8HmJ5UX30xFktmvlLgSrzsGIzSiYiAYH8eU6epJGr/xjYD +9GE6G9EcL+/NTc0ziPhIUtVm+h+kFEHtQ1VeOFEuVuWAukPNXRFDrYOMfeR4U1h7 +olT58U3+IlctreKavXr+7bPPhbhvdB7FPM8xusaPfwT6BG4sTqHyOPL5a3eXW7Sp +tuVD87KNHKo3skWcbb78EVQt7MfCMTam6X2O4Y0y1da51kOht6qRaeJHoaAjXB4V +pY9lJSQA+Km5uYu+FIwn89z7P4j2aUNZFq2UOSwuViekSR6lfHvortyh8YFrSRUs +Q4berZUICDp0Ek2BiJirWiPXAp9uxwXms9UbvQIDAQABAoIBAAUTnfMAUpU7b3IM +7C1NPa/x25SltVxjbh67AowN8GT3qku9y4geciq4Lk3ID+IYSVZ6egwGpEs7Ohvw +4Wjc3rcwzdVJiK4aFnx9cF9FZEdIWGT76JTGQQn9O4eY3cKQn/GfhY3qSkuGlVQf +URLnJ5jdxrEa4wXiP8h/QJ1/XY8v9en0uWf/18fMHyzImpd8dqgtTdomSQLjM2ie +1k9Fllh/5Q6e1Kd5mn5QP1QPC65Z78IskOot9Wrg0sk19sWiFMsopgJkQg66BlnM +Fj8A4HPS8Yylt0oXJ5yytUYfvZNDRANZRiAz9mfgQw9oapJLnCLt3Awi5bcKJIVk +/nTccEECgYEA8lSPFeTyzid0sIextXjjHvV4riSZKAHAa5M5DfsPkXLQfbEF4zog +FLZ6c85jUCk7/vTzDRXr38XKaGbAg/1M3UJk7lS9shh1Zdo0+Hshq7rNICNLKz5H +/u1/0gkxqqJcpXaoOHYN1hM7HJrP3uZFDIA5ZrB7JD20YDCQ/2PWrS0CgYEAwoHi +qEKKXOol7FIiG1upTjPaWMTmxtmSs2/pobMVf+ZGDMPts0pbqDegXruaNFUrUQOG +9yvrjA3a2QaKt7R5eDvHAafeMeu0WcHaqUWtIueTtEuduK4fXsCGONBPEGJu+Ct/ +BOCV/W8MAmzIOvffT7Jg1HWsZyAstoikPi1w4tECgYEAuvVmFxw1/7sNGgz2m+2S +PJZh7uipiOYhEF3bTN//mNWd6PskcbSsf45xVttKX9QQR5mv0s6w1koA6R8tNCe+ +n43T1NRoLfkUyenZqENHLPjHvR29prU8Un/ld6REP0NYewfarQTXk+vuVRlTesLp +TsW2g3Vw6/r3KKcPlxntzFkCgYEAqqkH1BY+DHQtPgJ6hoKQNFtuswBgdAymmOYS +mZvlu0iyIbUvNGaDsT7NaRE1pcEstnJf0zMoAsSNRmpk//ZLteDNJXjCjg5/OVnL +n0XROZTylfjatBWi1KIbonGzTW7warLPSdo8ABeU8/O6Y3Lk7qpWJ1PwJrOmR6nw +YdXA/GECgYEAsSokOABGGLQ2zOmDYPDKrRenLho8cSe4g/CF5b6x5MXfJvvBfI/M +hOS/2AFj7UkOSVM9B9QH607F67YJuBPNGvciCatqY/Bcs6ViuuyqG9U4O13OrrmA +oG8Cho3Zy2v3udaLJ6O5JYcpX+hdvkwoG5j/8hLrzkD/3AA12z6QnV0= +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u1.pub b/t/keys/u1.pub new file mode 100644 index 0000000..264c1f0 --- /dev/null +++ b/t/keys/u1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4HweYnlRffTEWS2a+UuBKvOwYjNKJiIBgfx5Tp6kkav/GNgP0YTob0Rwv781NzTOI+EhS1Wb6H6QUQe1DVV44US5W5YC6Q81dEUOtg4x95HhTWHuiVPnxTf4iVy2t4pq9ev7ts8+FuG90HsU8zzG6xo9/BPoEbixOofI48vlrd5dbtKm25UPzso0cqjeyRZxtvvwRVC3sx8IxNqbpfY7hjTLV1rnWQ6G3qpFp4kehoCNcHhWlj2UlJAD4qbm5i74UjCfz3Ps/iPZpQ1kWrZQ5LC5WJ6RJHqV8e+iu3KHxgWtJFSxDht6tlQgIOnQSTYGImKtaI9cCn27HBeaz1Ru9 g3@sita-lt.atc.tcs.com diff --git a/t/keys/u2 b/t/keys/u2 new file mode 100644 index 0000000..02486a6 --- /dev/null +++ b/t/keys/u2 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEA4/t3WV4q98MlYV9Jbvbf7gE2Dzit6dD8xHsWBh2wTmggQM9I +2RsPp1tTIoOpt4YdlTzV41577BKLVMvqG7AxnVljC7+m7PrT2YmxEGrrbcrHebHu ++pwh0lkN/CIz6MHjWzLbFiRoHhKh9LyfSiKxCgJ3dyjFgAnA+wrXkwIfPRG8qOLk +sb1KUsMdfYm0OUEC/mHU2dsIS5HIY6xhy5Q5BXWgkfYUK7LL7Eyw6ffLhvq4U9tv +Qv+u64Qowew4BY4B8rvyb73iEGcInIF2JRY0jVC8yJFejTJo2KKlXxu8MyAtAhth +cHE6gA7xmn1d8TF7sxHDNqprvD9mLTjUVdz1owIDAQABAoIBAQDEGaWLZYioHV+l +5gSQQiJT4w7RAPv3RyBlEUrcb+UbTE2R8brDpJdOaSuVYJM3nVEM8Ys5TChj43+d +rNjugBvtMNoVXQEEjqxzThDUAmQHyIjUkMzzHCGrgZaZ7gGgkEY0SAZTgXVdiMFu +dmC9sCGAbqa8BIH9pGYuiiDr/sNIDr3ekyqyuSA0Aa2JIrEx1TKFXF5JtGseU9S6 +bUfesCpoWGyVdulr3NbsLM26eDCsZo45OF5QdpTtU99xc9K4gsOre0ZtqEJMVGlR +2nDQILCroH93GabSIW+fiUZD2lO9BxXAM0NA730ODQWyM8IgoWrqxGUuFz40l7X1 +teB7yRwBAoGBAPkkF//ZaEUUyCX6+a6TjaPQ1atemQCxHtIm1phfIM/u2uANqeOP +Z+N3dM0TL53lRx/xbT0dO4sfF73XcF0sYkQj9rep28Q04W8Z6iC/I+jQAJJ0bYl5 +skUGTECxIPLmKOciQF7N5PUGvxhI4oq/0vgDYFc+NW18mow1Z66H1PkjAoGBAOpC +P9EJXEFKZY1lK6ZL6wvJ6tJaPWAuiQlkOdj9wLvvzywQPlEdBJ1k0q+ndAqpR6WJ +eTlkP/bzDpLRi+l4PEVTsFlpxjcfVn19RRabsKTNIS0usl1el/uQP7u4rRimBCz5 +MO72GD4ARM5CgMZZMGU5AXmxKEM9feTcUNss3RmBAoGBALTYsmMRmVKr5y1KpPtI +OER1TuR6Ym3SJCE/9/3a76KAK3j/8hYw/qRrDenex23CBIL3aOg31AUEqOMxA2te +0GXOBUUEk3Y1PH69POpQVOymMAQfZ3OnVvQrwiYjbVtkHsTIZBltM4l5QDWMkoVN +AQLu0HwDuBylmjm0enKCPuIpAoGBAObd257bprwB4gtzhY0ijMbVfENLA+nictN6 +nzgm/Oc68+XtLD0sZ/vl/W13jnljU2TlEz9oeVGbQOWY9lZlVKDOVaIJCHwSul56 +MriRP4lrUCMDPm2eaBJYmzcaTh1YoAzimUMn7cRM54KPL/JKu9NGVxnjala6J3SB +XH5kvJIBAoGBAITZe45tIbVkGjK3jTjgBfaqFfhyws4L3QgRbjqpy2h9nLEGHMYV +RkIt6bCkw6cy+8TnYB0iG9lcGP1S5E4C6zpTdldi/5tGl7uu9qfEz4hxRpRbJ4Q0 +nwZzUkgBaLxAiLHfntBpwk16UvHkCpo5hiKzSLzxfReXzQPUUudGRnsG +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u2.pub b/t/keys/u2.pub new file mode 100644 index 0000000..916dcf5 --- /dev/null +++ b/t/keys/u2.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDj+3dZXir3wyVhX0lu9t/uATYPOK3p0PzEexYGHbBOaCBAz0jZGw+nW1Mig6m3hh2VPNXjXnvsEotUy+obsDGdWWMLv6bs+tPZibEQauttysd5se76nCHSWQ38IjPoweNbMtsWJGgeEqH0vJ9KIrEKAnd3KMWACcD7CteTAh89Ebyo4uSxvUpSwx19ibQ5QQL+YdTZ2whLkchjrGHLlDkFdaCR9hQrssvsTLDp98uG+rhT229C/67rhCjB7DgFjgHyu/JvveIQZwicgXYlFjSNULzIkV6NMmjYoqVfG7wzIC0CG2FwcTqADvGafV3xMXuzEcM2qmu8P2YtONRV3PWj g3@sita-lt.atc.tcs.com diff --git a/t/keys/u3 b/t/keys/u3 new file mode 100644 index 0000000..163bdef --- /dev/null +++ b/t/keys/u3 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEArK6uQkgoAnS+w6vs4DmvdOfcGdFUzh3ivHAXHug7sjjKGUB2 +KwcXNwcmJPtyNXM0BJ9ZoOJSqJo5cWLMl7Qn1HBubuyM02Id9EqeJu1Ytn/i1XFY +NMp8fj4wma3LZTdeOy3ockbefkw6VQUq3cheXIeaVpT91jsuyIaE0ejWkjGMu596 +zXFQZYTSO28TIYghRvhXGq5W9hJ3V2k+ZAQ/AsWQqjK4XruICi+RGRpKj59ECI7V +zIrq4MUEti+3LldaY6tyYsTGQLtFJYu+l/ZavkrM979cPOxLeoi8ZHPTPKh6asdr +ZXA2J6++IyIgNy2Q4sNxTmkIlU/EDysvTTGdMwIDAQABAoIBABF+NJr0UlFFYFnU +Hc/tKBAQuORIp22l62Upeb4gyoNYa2i5df8P3dMuPzf53Oz7OabKObspkjQQQ4dv ++cfYcTx9E0LbZby4MM6hjHnnC1iZhfIXZFccuBXV2PiIeZVMUZhvIyAIe9uRf0tD +lb8X4C9BcWoZ98ju/+NCdUwKaUov3jXc32hmsAhK+dtqBE+lwccX9keJQcncf+6j ++2znpDAJdaF70dM0DidbkmXmOTgH6LjjqrYRTAUnVsvUyDsr5YhopwOHs0E95YB4 +RzO7V/DA8H7DQ+XE20Iqp2i6dSbRQQPxbQLvJG4BaITIqb1L8/WJ6N75O/uns4rE +Y8WVwWECgYEA1gDDP70DqvVMu6KDvn18pvfxzxJ3jccYcBZITV/E6xgj1AbH7fdA +0iwvG6jQ26DTUBfLBquZ2fCCyI44OUpZ8GZu/wYBn5CQjYtr1vJ7zHKM3dVf+POT +cGgOVDIHopCCX5Dwb74OpbkTV4g/WxouClvz0Ovobq8NncSkJJkyGEMCgYEAzpIF +oj2AVMWDd6NL+P8e+QXPYEvJ/trAHoAteI83Eof4ZOC/sBr7Gf3c6nM7ZJbCXGhJ +NCCr4teHJMg9DThQ1KxKn9qEf9dWiAE+HCouaZU98Z0UcLQ+tgPeBh/Dw7rTF3M2 +7A/LtLYbg1MoPCZNj6V4qGjmNWCy2lku1ZGBUFECgYBD/4oKvqxjrf3rwP/Lj2QE +SdRzz5JdYl3Jf8sJityvNsRropv0aRQXtCJjz4hNwRRj5quEOxJvxZRI1afXzGA3 +mtS6A9aQNQc5couZiQL9O4i3FA2itQKsPOQQrLTwWqqSYyOC3gkZb21N6uT2taLb +d8xJHiyEvuq8rrbZSjQ4sQKBgEkUDZws58aVrYHYqlrnXny4mnm1tjtMBiWEMRHy +kIgkxDJj9EyH7wdt8QacR4m5b/8jAarIWCbDGtNfZ4HSx33FigztUGytsLYiwmdS +YOMHYkeky4NnsLvRuG0wNaB76ovkPazblbRTrH4UICrPXicQYhQqMC74C64FWPVD +KZ1RAoGAE/vKCHCzPegTT9gr2aaIrhLPUUZboOF4gHajYr9Scr176nJhpIvP/0Y2 +yQtqfas5lID8ouqXb+oL0Q4Yi00hdu+TRYQHm3M+2UL7wgffR5H2vfhk24UUDfV5 +0qQjNKp3pNUWZdiZ2J+RGUszXt0THfWbWI/ntwnG5QchqXCqYEA= +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u3.pub b/t/keys/u3.pub new file mode 100644 index 0000000..e97645c --- /dev/null +++ b/t/keys/u3.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsrq5CSCgCdL7Dq+zgOa9059wZ0VTOHeK8cBce6DuyOMoZQHYrBxc3ByYk+3I1czQEn1mg4lKomjlxYsyXtCfUcG5u7IzTYh30Sp4m7Vi2f+LVcVg0ynx+PjCZrctlN147LehyRt5+TDpVBSrdyF5ch5pWlP3WOy7IhoTR6NaSMYy7n3rNcVBlhNI7bxMhiCFG+Fcarlb2EndXaT5kBD8CxZCqMrheu4gKL5EZGkqPn0QIjtXMiurgxQS2L7cuV1pjq3JixMZAu0Uli76X9lq+Ssz3v1w87Et6iLxkc9M8qHpqx2tlcDYnr74jIiA3LZDiw3FOaQiVT8QPKy9NMZ0z g3@sita-lt.atc.tcs.com diff --git a/t/keys/u4 b/t/keys/u4 new file mode 100644 index 0000000..a669e34 --- /dev/null +++ b/t/keys/u4 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA2Wg3bl7T0C8VuR8HdbAqmwvQH4/T/maaqlQeJqcATRgWQNDv +VthEasW5Kx8DzcSVRWS0cJ5EpTLGvrs84aXgdvg6TwFZKO/ujrkFDZmw9hd4I/Dv +0dp/Y7himS8vAvnnWfYyqJBiX4plEx9Kg8oWHwy8KK3HHKoO8jAWAHOWO3GMB8BV +128VLovGB6Sp99GrgltzP9UhNRHt/doa3ve8+fDk782SandVTTR9MNLt7qhMSOKr +bmoFWtL36W5hmVjFGTZC1ZNIU6PiqygUqPtyAYblwv/nZ35s72KmxP7Seag3SsuZ +UPnRjM4PZ+7YE7tcPTOUxcct8xuTZXgjh2WiNwIDAQABAoIBADSrR8qIVJ452fRo +LQF49UlsmjYbPQuDxfJ/wHIywSLsM+/t7h3G9QQ89Hga4mwGNPeDxycFYLH41Cc+ +6yfrbK7FwjKDrBr7zXpsHmpGEpX755Ile6QGYBhDgjeEM8pvynmD6I/nsr1cpNH2 +IbI90hAhoK/mMbejB03rEll3pyytCgEvJQsu847dTNTZN+PHsumPr9HERi/dDrbw +JrDSH2tbOvYw4UW9HLFfZAB+IgMo34WXf6kcUY69wWQbxnv1e7KHnYkxIuLCGZ6x +Lc9APM97f4atlGt+mAtqK97CJB1AqoIE3KpPq1x+gpFSBNEE+9U9KnItuSqF3mG4 +vq/VjIkCgYEA+ASubOgzJaAldNz3dinLYK6MLB+V1R+RpkDc3+OgfF5mgg11ZtpE +/DO1Ndpf5dOYB4JYGfhC9+xomHGsQmbOsIUprWaAR/xaO6Bgxw7cxkOBIadsrdwu +MWJ6h3gN72zuhKPERpUVVH7ZH5KQfZwxEMgquUkGdEwumU4rMXeCFqsCgYEA4GdX +qI/5UNXEPnXk3dDWNzNJO7gRoonU228IM18qz5ZKMRIKp6Sq+wIuO8+aQMr2K0rO +RMQk+lLOZ17omaWh9ysDPzTCWRrjOiDOnYWDPCclTeqSnEk8uuWyppDGNXgQ6osM +LnwJSeP8+1EDgkKf5zoiQfcyBc0v8PSRSdTfEqUCgYEA2PqHeqHd9UHY4xdZq1e/ +JLMv0H5Ff/GhY7iFQ54JziRsO8T4e+Xiyl2WYCnPEer+qzseRoIKXInHq+5uzJzS +oF2va5MsEU41xsp1QFDBVvbBpyapDqV9CBlmptOiJV/Af+wiD7nnskdTPqrjm/Ck +gFEOB5Fagy4O6nIXmaw69AcCgYEAhVSBndKlZKUOa7oqmKzLipK7UXNFbxiL0zE+ +Yx+JVTvLqyo4EHFjca5TABCSayrsZr6UngEYo27t2jdm5lumRzBURoq3aq/yEIiL +msZIOkZcANZ988QEBFwT8KmWSxCipGinfTsPXcrLdhslhZDGZ2GAF0ejfhTzBiyZ +4o9LV00CgYBf46Z2M1fWI6Tc8HUKr9WoyUmTmUtHUFPt9IZ2CKm2czoltpku7cC7 +ztlt4LmVPKv1UUavbC/nCz6s1ylOkY0rdm45FSYXKDYPMQqzQix1jG4GZJjM9En+ +M848LupnHBFsmHyQqyyRlsg1gb+wtvAw7Y44wcfAhGbiAT5DVX+zxg== +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u4.pub b/t/keys/u4.pub new file mode 100644 index 0000000..06f3648 --- /dev/null +++ b/t/keys/u4.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZaDduXtPQLxW5Hwd1sCqbC9Afj9P+ZpqqVB4mpwBNGBZA0O9W2ERqxbkrHwPNxJVFZLRwnkSlMsa+uzzhpeB2+DpPAVko7+6OuQUNmbD2F3gj8O/R2n9juGKZLy8C+edZ9jKokGJfimUTH0qDyhYfDLworcccqg7yMBYAc5Y7cYwHwFXXbxUui8YHpKn30auCW3M/1SE1Ee392hre97z58OTvzZJqd1VNNH0w0u3uqExI4qtuagVa0vfpbmGZWMUZNkLVk0hTo+KrKBSo+3IBhuXC/+dnfmzvYqbE/tJ5qDdKy5lQ+dGMzg9n7tgTu1w9M5TFxy3zG5NleCOHZaI3 g3@sita-lt.atc.tcs.com diff --git a/t/keys/u5 b/t/keys/u5 new file mode 100644 index 0000000..ed65131 --- /dev/null +++ b/t/keys/u5 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA41A0bY6+0akNSJlR2PeRATNtncARXVOUar7CNaxwPqVXQR1+ +TmU9evmIEkRLf0kFAa7L3QDFroFu4sDiSJjvfYIkHdoxO4Fk128PJBhObIXaarKc +UBIZ29/8I3dTedq5CY/YHL/AjaT+0VktWX7YwigvGDamrXAKqjnW1mWKp6TNj8bp +vppBvj3yvdYoOLYvCs/SKfKdlayngiihTbKELPcRu8NzYxHiF2b/u0t31evdP0fA +/apAslKhHdCm8ScZJuyIdztcogBFE5dGm0h3qlbJKvKT8JmTcgQ5TBBcmqPVmUkm +nPvIzd2G7D1smbExG7LnqekHcS9dvaPnF0B3oQIDAQABAoIBAGl1e21crXDN0mjd +INjdOnvpJTDru+KldRT0/VszbjvSL6H5EfFDDPvxqsx2vOQHt3fpZZFZ21yzlgND +Y3g0499BsonbAb5OsL82OjsPv8qfaw7XYKfRTgfxaaP2p1bAP9qMzsG/wJC2fLYZ +fm2n6N5jED5WlIugkIIbJW4AXAycDB2ZU0xtPTHJ8nrSj1otDCsD2VbbVHXN7H6g +HrTqP4RqD+uVDIxSrXllz4Udwe/I1wUrvY9HjH2qS6Liqb5kw8SOf4a2et18+KB2 +NNk6ZEAlmOx1ddC2eZPxm8XxAxdSwTggcwvtixCeoXR53OwTZAZ8BBzwusrMklZm +/n5zPWUCgYEA88Lf4i0WFu9h4FnQ6qxCAKDS1XyRpCYt5cyTpZPMV3LuUV0DPS3K ++EZeU689BSOiwav8omCtrrrOHtE9fHjOH85Gd4IRyOwUQWI7RLpFiI8Lbs4tMbP5 +k1UOzzTji++N47XfTTQVdwHbx4jac80LVavbRd3AKd6oRjc/gQOpgasCgYEA7rnr +uoWqT76xVNLmE+g93x33hzES2HitgjHzvm5B2Cu6fwlXi7cSnLEWVn7W5QXEuhek +6uhq4NC0ahL/qAyJsFhVve32qrR7yacDVlMWZ5iQPfqtvS9CsxyafADBWsgeN4L1 +5oqQofNlh0l+UvMFp8INNbKfxTOPKCMfGxIEd+MCgYEA7UsJky380PrbtwD4JVrn +LaFhXL3VMYyRJaFPIeKNC5wwbzgyjP3lFme6L5Dpv/T+3bZFSvT+XpgvS0S5rFAV +qFSvuGsAUS2wUi4EMFV8lwFZSdafnEDtdgVZU1DTKkhbQg6sgIVxV9aRUt7gedZj +cFTKMms6RAgim6fww/ECs90CgYBdI1pt9jJhVHPZNUMgpy5ke0uUijfhDwwazKRd +OqUj0sO7RojKcM2pJoohivEKf3qmZA0qvSzds2+AJxNpnCKoE364UDw5k5rsLOXn +axlFp8c29zOLqQGr4c//60eExKjNXaHUpWESXmTRKIJJmJkvP01qEtu0043ZygIb +zKbDowKBgHhacDR3aat3kkaTp/4AhekhDhMgZ1u2NIi0ycsA6zRlmFucHMwtKCM0 +VMuu40D9N1lTmcdndNKkTJx6CpbS7g/y0ctkyGwgFdmTjjHYl1nATt9G2oRzKx6I +nQ+sBUwBPieIqEmiFh+KAsDox6plrtvhSSwp8FYLWBJXZHlLJIsd +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u5.pub b/t/keys/u5.pub new file mode 100644 index 0000000..96a0045 --- /dev/null +++ b/t/keys/u5.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDjUDRtjr7RqQ1ImVHY95EBM22dwBFdU5RqvsI1rHA+pVdBHX5OZT16+YgSREt/SQUBrsvdAMWugW7iwOJImO99giQd2jE7gWTXbw8kGE5shdpqspxQEhnb3/wjd1N52rkJj9gcv8CNpP7RWS1ZftjCKC8YNqatcAqqOdbWZYqnpM2Pxum+mkG+PfK91ig4ti8Kz9Ip8p2VrKeCKKFNsoQs9xG7w3NjEeIXZv+7S3fV690/R8D9qkCyUqEd0KbxJxkm7Ih3O1yiAEUTl0abSHeqVskq8pPwmZNyBDlMEFyao9WZSSac+8jN3YbsPWyZsTEbsuep6QdxL129o+cXQHeh g3@sita-lt.atc.tcs.com diff --git a/t/keys/u6 b/t/keys/u6 new file mode 100644 index 0000000..86deee7 --- /dev/null +++ b/t/keys/u6 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxyRjRT8RoSDnAnbZdrTjXhBMrkfNfolWFqc3qAjAo8Tmp7Ns +n7R+KCl31RDkC9u4ll4AOfF9bIdP9ovDVvXoTMoiOdPTYqsnaMBBpDH04vxID2U5 +rEOhSzbvTazzaSlYNFED2a9bMfULuF6WxchFPBQGhTZaC8cDAkS5YYXvjHXAn1GN +kpv0k5qltD8vjJoM7Mg5JLm84WejPQ5CmwOXqsQj69ZUZEyA8J3oqWVYkEhpYkbK +IuzaHkrTC0PUQJ8MS3wM8358bWp53zYFJMkyL1a/zJQPOMaDiFTXXj1Uue/5ZkIM +/fkkOMLbaCgEFCMbL8HkVEq4Q0dLhJKl1IdzbQIDAQABAoIBACQd91shuyLMAtmx +kHM1D1+J+T5Ki3x9j/1/ylpRbA7HsUWNBxBX/eFu0+ryq0lzSiELX2Mi5yp9yATh +CEaHRuBWcKqoPlhQzk7zP3R2EwHv22nfY/xYL7KiffhKe8MA2pxybQ5X/WQsGzoO +/a1VSylAQIZ8ewxTxbntmOmVDwMcLZdY5X4NAmzUw1lFD2TVOlHyhjvhgIzhO1tp +fBvsX3OL+Pk0tTQebuhoqEy2R01BLc+0ZFAKAFZpZgualqZqjyIU0isQsEo+EK4s +Pn7Ccxy+156aWAEdgD5Aj7oMn3zA9YIt/JoX8muaceUOj3E7B5ZKBtYZkFPVnKO9 +rPMNTIkCgYEA/v5IeFDZjcAMLWqhuPekcUQYMXc+I0ZTn7vT3aksWxhARG96CPdG +HNLMw4y8yZLnCAsdKUJGtrVcn4y4K0FzUYn0tDm9cmtFoFcQvLPp4mR1kB84urIG +7qEwv4djeLhl0cYsqMpyl7qflbTVwKa09Fy7HX9ZFL58nXVX63uRXlMCgYEAx+2o +L/GKkKgRPpCudsGoplECjzqP4SWHXaYVOupVzpnvxFKVcpBl6Un6p6U9V6W16k2C +XUg9XqFdleawfAwGQ2D/Ip/u4r8GKOphv0QCjO88ld15Ky9mr9Sk4/Z2EAMUrmKg +CS4hGBF7VIeA3FnzJwLkYL0+WQKpKBt/zojQLz8CgYEAnmtEgttYDeTeq+iviMbx +9xyjGzhF9oxer8J1oiTUVdP/OYU4gBGAEbA1Xtg1AdauiiS9fUCbxi9u2AEI+naz +OllHGiE1Pby/iRoOX+42xFw9XcjH6dVo0SB7tMJcXkfRmj5QyJzeDL35H301v3bS +vW5PIchYg7bEnN6mPLqMWdkCgYBVWaX1Yb5v5vAFr6prVF11MxxOnQeTbHwPhLmH +f0bGfn0XaNIYKID5SPXS3/4CDuJMdm5y+EYKwgS729H4AwIhfaUt2O0Yq8gra3Pz +PUuBcxiAOh5iS0ghRDxofW0FhOstTzlW8fR62+u0uGxQpa3iN5/blK6rPTGNx7+W +Il4N7QKBgHRTxUZraVt3n0kJ4+tdc+F8XK1wLyVC9PmtW+28tiyx14k8o8H/6W94 +r3lyI13sUnvW6zU0VMAALQujRcJDLac/zSUdFGmgU3P3QatU5yUm0Xpt1uFM49Zw +xZLBTDJxe2Qrc3Z3SKReRqxLz6tTU1soH7TVw/S36tBovKjTw7Cc +-----END RSA PRIVATE KEY----- diff --git a/t/keys/u6.pub b/t/keys/u6.pub new file mode 100644 index 0000000..de5b06b --- /dev/null +++ b/t/keys/u6.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHJGNFPxGhIOcCdtl2tONeEEyuR81+iVYWpzeoCMCjxOans2yftH4oKXfVEOQL27iWXgA58X1sh0/2i8NW9ehMyiI509NiqydowEGkMfTi/EgPZTmsQ6FLNu9NrPNpKVg0UQPZr1sx9Qu4XpbFyEU8FAaFNloLxwMCRLlhhe+MdcCfUY2Sm/STmqW0Py+MmgzsyDkkubzhZ6M9DkKbA5eqxCPr1lRkTIDwneipZViQSGliRsoi7NoeStMLQ9RAnwxLfAzzfnxtannfNgUkyTIvVr/MlA84xoOIVNdePVS57/lmQgz9+SQ4wttoKAQUIxsvweRUSrhDR0uEkqXUh3Nt g3@sita-lt.atc.tcs.com diff --git a/t/ssh-basic.t b/t/ssh-basic.t new file mode 100755 index 0000000..2747185 --- /dev/null +++ b/t/ssh-basic.t @@ -0,0 +1,51 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Common; +use Gitolite::Test; + +my $bd = `gitolite query-rc -n GL_BINDIR`; +my $h = $ENV{HOME}; +my $ab = `gitolite query-rc -n GL_ADMIN_BASE`; +umask 0077; + +try " + plan 26 + + # reset stuff + rm -f $h/.ssh/authorized_keys; ok or die 1 + + cp $bd/../t/keys/u[1-6]* $h/.ssh; ok or die 2 + cp $bd/../t/keys/admin* $h/.ssh; ok or die 3 + cp $bd/../t/keys/config $h/.ssh; ok or die 4 + + mkdir $ab/keydir; ok or die 5 + cp $bd/../t/keys/*.pub $ab/keydir; ok or die 6 +"; + +_system("gitolite post-compile ssh-authkeys"); + +# basic tests +# ---------------------------------------------------------------------- + +confreset; confadd ' + @g1 = u1 + @g2 = u2 + repo foo + RW = @g1 u3 + R = @g2 u4 +'; + +try "ADMIN_PUSH set3; !/FATAL/" or die text(); + +try " + ssh u1 info; ok; /R W \tfoo/ + ssh u2 info; ok; /R \tfoo/ + ssh u3 info; ok; /R W \tfoo/ + ssh u4 info; ok; /R \tfoo/ + ssh u5 info; ok; !/foo/ + ssh u6 info; ok; !/foo/ +" From 5ebb981efabd201b2bffde1edf88d6f19ff806e7 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 05:28:56 +0530 Subject: [PATCH 538/850] new sugar -- keysubdirs as groups. TODO: add appropriate commented entry to Gitolite::Rc.pm also --- src/syntactic-sugar/keysubdirs-as-groups | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100755 src/syntactic-sugar/keysubdirs-as-groups diff --git a/src/syntactic-sugar/keysubdirs-as-groups b/src/syntactic-sugar/keysubdirs-as-groups new file mode 100755 index 0000000..d16dc78 --- /dev/null +++ b/src/syntactic-sugar/keysubdirs-as-groups @@ -0,0 +1,31 @@ +# vim: syn=perl: + +# "sugar script" (syntactic sugar helper) for gitolite3 + +# Enabling this script in the rc file allows you to use subdirectories in +# keydir as group names. The last component other than keydir itself will be +# taken as the group name. + +sub sugar_script { + my $lines = shift; + + my @out = @{ $lines }; + unshift @out, groupnames(); + + return \@out; +} + +sub groupnames { + my @out = (); + my %members = (); + for my $pk (`find ../keydir/ -name "*.pub"`) { + next unless $pk =~ m(.*/([^/]+)/([^/]+)\.pub$); + next if $1 eq 'keydir'; + $members{$1} .= " $2"; + } + for my $m (sort keys %members) { + push @out, "\@$m =" . $members{$m}; + } + + return @out; +} From 6624d35cf99f9471e7d5061ff1bb42077ae8ea0b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 06:07:41 +0530 Subject: [PATCH 539/850] info command deals with groups --- src/Gitolite/Conf/Load.pm | 1 + src/commands/info | 16 ++++++++++++--- t/info.t | 41 ++++++++++++++++++++++++--------------- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/Gitolite/Conf/Load.pm b/src/Gitolite/Conf/Load.pm index 1759214..9b38880 100644 --- a/src/Gitolite/Conf/Load.pm +++ b/src/Gitolite/Conf/Load.pm @@ -119,6 +119,7 @@ sub load_common { sub load_1 { my $repo = shift; + return if $repo =~ /^\@/; trace( 4, $repo ); _chdir("$rc{GL_REPO_BASE}/$repo.git"); diff --git a/src/commands/info b/src/commands/info index fe52837..6672aeb 100755 --- a/src/commands/info +++ b/src/commands/info @@ -19,13 +19,23 @@ usage() if @ARGV; my $user = $ENV{GL_USER} or _die "GL_USER not set"; my $ref = 'any'; -my $fn = lister_dispatch('list-repos'); +my $lr = lister_dispatch('list-repos'); +my $lm = lister_dispatch('list-members'); -for ( @{ $fn->() } ) { +for ( @{ $lr->() } ) { my $perm = ''; for my $aa (qw(R W ^C)) { my $ret = access($_, $user, $aa, $ref); $perm .= ( $ret =~ /DENIED/ ? " " : " $aa" ); } - print "$perm\t$_\n" if $perm =~ /\S/; + next unless $perm =~ /\S/; + if (/^\@/) { + print "\n$perm\t$_\n"; + for ( @{ $lm->($_) } ) { + print "$perm\t$_\n"; + } + print "\n"; + } else { + print "$perm\t$_\n"; + } } diff --git a/t/info.t b/t/info.t index 967f4af..7171fbb 100755 --- a/t/info.t +++ b/t/info.t @@ -6,12 +6,13 @@ use warnings; use lib "src"; use Gitolite::Test; -try 'plan 35'; +try 'plan 45'; try "## info"; confreset;confadd ' - repo t1 + @t1 = t1 + repo @t1 RW = u1 R = u2 repo t2 @@ -30,23 +31,31 @@ try " "; try " glt info u1; ok; gsh - /R W *\tt1/ - /R *\tt2/ + /R W \t\@t1/ + /R W \tt1/ + /R \tt2/ !/t3/ - / R W *\ttesting/ + /R W \ttesting/ glt info u2; ok; gsh - /R *\tt1/ - /R W *\tt2/ + /R \t\@t1/ + /R \tt1/ + /R W \tt2/ !/t3/ - / R W *\ttesting/ + /R W \ttesting/ glt info u3; ok; gsh - /R W *\tt3/ - !/t1/ - !/t2/ - / R W *\ttesting/ + /R W \tt3/ + !/\@t1/ + !/t[12]/ + /R W \ttesting/ glt info u4; ok; gsh - /R *\tt3/ - !/t1/ - !/t2/ - / R W *\ttesting/ + /R \tt3/ + !/\@t1/ + !/t[12]/ + /R W \ttesting/ + glt info u5; ok; gsh + !/t[123]/ + /R W \ttesting/ + glt info u6; ok; gsh + !/t[123]/ + /R W \ttesting/ " or die; From 9a8a86306b9976476ef59564e3db4062fdbbab15 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 09:15:02 +0530 Subject: [PATCH 540/850] _system() is less verbose otherwise things like 'gitolite access' print extra junk that is confusing. --- src/Gitolite/Common.pm | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Gitolite/Common.pm b/src/Gitolite/Common.pm index 2260e41..6fcfcc6 100644 --- a/src/Gitolite/Common.pm +++ b/src/Gitolite/Common.pm @@ -105,15 +105,19 @@ sub _chdir { } sub _system { + # run system(), catch errors. Be verbose only if $ENV{D} exists. If not, + # exit with if it applies, else just "exit 1". if ( system(@_) != 0 ) { - say2 "system @_ failed"; + say2 "system @_ failed" if $ENV{D}; if ( $? == -1 ) { - die "failed to execute: $!\n"; + die "failed to execute: $!\n" if $ENV{D}; } elsif ( $? & 127 ) { - die "child died with signal " . ( $? & 127 ) . "\n"; + die "child died with signal " . ( $? & 127 ) . "\n" if $ENV{D}; } else { - die "child exited with value " . ( $? >> 8 ) . "\n"; + die "child exited with value " . ( $? >> 8 ) . "\n" if $ENV{D}; + exit ( $? >> 8 ); } + exit 1; } } From fb332a6c761661373562682afc27882521608be5 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 09:28:07 +0530 Subject: [PATCH 541/850] (!!) neat little 'access' command... ...makes it sooo much eaier to check access rights from external scripts --- src/commands/access | 53 +++++++++++++++ t/access.t | 152 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100755 src/commands/access create mode 100755 t/access.t diff --git a/src/commands/access b/src/commands/access new file mode 100755 index 0000000..f59ac6b --- /dev/null +++ b/src/commands/access @@ -0,0 +1,53 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; +use Gitolite::Conf::Load; + +=for usage +Usage: gitolite access [-q] + +Check access rights for arguments given. With '-q', returns only an exit code +(shell truth, not perl truth -- 0 is success, any non-0 is failure). + + - repo: mandatory + - user: mandatory + - perm: defauts to '+'. Valid values: R, W, +, C, D, M + - ref: defauts to 'any'. See notes below + +Notes: + + - ref: Any fully qualified ref ('refs/heads/master', not 'master') is fine. + The 'any' ref is special -- it ignores deny rules (see docs for what this + means and exceptions). +=cut + +# TODO: deal with "C", call it ^C + +usage() if not @ARGV or $ARGV[0] eq '-h'; +my $quiet = 0; +if ( $ARGV[0] eq '-q' ) { $quiet = 1; shift @ARGV; } + +my ( $repo, $user, $aa, $ref ) = @ARGV; +$aa ||= '+'; +$ref ||= 'any'; +# XXX the 4th one below might need fine tuning +_die "invalid repo name" if not( $repo and $repo =~ $REPONAME_PATT ); +_die "invalid user name" if not( $user and $user =~ $USERNAME_PATT ); +_die "invalid perm" if not( $aa and $aa =~ /^(R|W|\+|C|D|M)$/ ); +_die "invalid ref name" if not( $ref and $ref =~ $REPONAME_PATT ); + +my $ret = ''; + +$ret = access( $repo, $user, $aa, $ref ); + +if ($ret =~ /DENIED/) { + print "$ret\n" unless $quiet; + exit 1; +} + +print "$ret\n" unless $quiet; +exit 0; diff --git a/t/access.t b/t/access.t new file mode 100755 index 0000000..d63eb19 --- /dev/null +++ b/t/access.t @@ -0,0 +1,152 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; + +# basic tests +# ---------------------------------------------------------------------- + +try "plan 185"; + +confreset;confadd ' + @admins = admin dev1 + repo gitolite-admin + RW+ = admin + + repo testing + RW+ = @all + + @g1 = t1 + repo @g1 + R = u2 + RW = u3 + RW+ = u4 +'; + +try "ADMIN_PUSH set1; !/FATAL/" or die text(); + +try " + + gitolite access -q t1 u1; !ok; !/./ + gitolite access -q t1 u1 R; !ok; !/./ + gitolite access -q t1 u1 W; !ok; !/./ + gitolite access -q t1 u1 +; !ok; !/./ + gitolite access -q t1 u2; !ok; !/./ + gitolite access -q t1 u2 R; ok; !/./ + gitolite access -q t1 u2 W; !ok; !/./ + gitolite access -q t1 u2 +; !ok; !/./ + gitolite access -q t1 u3; !ok; !/./ + gitolite access -q t1 u3 R; ok; !/./ + gitolite access -q t1 u3 W; ok; !/./ + gitolite access -q t1 u3 +; !ok; !/./ + gitolite access -q t1 u4; ok; !/./ + gitolite access -q t1 u4 R; ok; !/./ + gitolite access -q t1 u4 W; ok; !/./ + gitolite access -q t1 u4 +; ok; !/./ + + gitolite access t1 u1; !ok; /\\+ any t1 u1 DENIED by fallthru/ + gitolite access t1 u1 R; !ok; /R any t1 u1 DENIED by fallthru/ + gitolite access t1 u1 W; !ok; /W any t1 u1 DENIED by fallthru/ + gitolite access t1 u1 +; !ok; /\\+ any t1 u1 DENIED by fallthru/ + gitolite access t1 u2; !ok; /\\+ any t1 u2 DENIED by fallthru/ + gitolite access t1 u2 R; ok; /refs/\.\*/ + gitolite access t1 u2 W; !ok; /W any t1 u2 DENIED by fallthru/ + gitolite access t1 u2 +; !ok; /\\+ any t1 u2 DENIED by fallthru/ + gitolite access t1 u3; !ok; /\\+ any t1 u3 DENIED by fallthru/ + gitolite access t1 u3 R; ok; /refs/\.\*/ + gitolite access t1 u3 W; ok; /refs/\.\*/ + gitolite access t1 u3 +; !ok; /\\+ any t1 u3 DENIED by fallthru/ + gitolite access t1 u4; ok; /refs/\.\*/ + gitolite access t1 u4 R; ok; /refs/\.\*/ + gitolite access t1 u4 W; ok; /refs/\.\*/ + gitolite access t1 u4 +; ok; /refs/\.\*/ + +"; + +confreset;confadd ' + @admins = admin dev1 + repo gitolite-admin + RW+ = admin + + @g1 = u1 + @g2 = u2 + @g3 = u3 + @gaa = aa + repo @gaa + RW+ = @g1 + RW = @g2 + RW+ master = @g3 + RW master = u4 + - master = u5 + RW+ dev = u5 + RW = u5 +'; + +try "ADMIN_PUSH set2; !/FATAL/" or die text(); + +try " + gitolite access \@gaa \@g1 + any ; ok; /refs/.*/; !/DENIED/ + gitolite access aa \@g1 + refs/heads/master ; ok; /refs/.*/; !/DENIED/ + gitolite access \@gaa \@g1 + refs/heads/next ; ok; /refs/.*/; !/DENIED/ + gitolite access \@gaa \@g1 W refs/heads/next ; ok; /refs/.*/; !/DENIED/ + gitolite access \@gaa u1 + refs/heads/dev ; ok; /refs/.*/; !/DENIED/ + gitolite access \@gaa u1 + refs/heads/next ; ok; /refs/.*/; !/DENIED/ + gitolite access aa u1 W refs/heads/next ; ok; /refs/.*/; !/DENIED/ + gitolite access \@gaa \@g2 + refs/heads/master ; !ok; /\\+ refs/heads/master \@gaa \@g2 DENIED by fallthru/ + gitolite access \@gaa \@g2 + refs/heads/next ; !ok; /\\+ refs/heads/next \@gaa \@g2 DENIED by fallthru/ + gitolite access aa \@g2 W refs/heads/master ; ok; /refs/.*/; !/DENIED/ + gitolite access aa u2 + any ; !ok; /\\+ any aa u2 DENIED by fallthru/ + gitolite access \@gaa u2 + refs/heads/master ; !ok; /\\+ refs/heads/master \@gaa u2 DENIED by fallthru/ + gitolite access \@gaa u2 W refs/heads/master ; ok; /refs/.*/; !/DENIED/ + gitolite access \@gaa \@g3 + refs/heads/master ; ok; /refs/heads/master/; !/DENIED/ + gitolite access \@gaa \@g3 W refs/heads/next ; !ok; /W refs/heads/next \@gaa \@g3 DENIED by fallthru/ + gitolite access \@gaa \@g3 W refs/heads/dev ; !ok; /W refs/heads/dev \@gaa \@g3 DENIED by fallthru/ + gitolite access aa u3 + refs/heads/dev ; !ok; /\\+ refs/heads/dev aa u3 DENIED by fallthru/ + gitolite access aa u3 + refs/heads/next ; !ok; /\\+ refs/heads/next aa u3 DENIED by fallthru/ + gitolite access \@gaa u4 + refs/heads/master ; !ok; /\\+ refs/heads/master \@gaa u4 DENIED by fallthru/ + gitolite access \@gaa u4 W refs/heads/master ; ok; /refs/heads/master/; !/DENIED/ + gitolite access aa u4 + refs/heads/next ; !ok; /\\+ refs/heads/next aa u4 DENIED by fallthru/ + gitolite access \@gaa u4 W refs/heads/next ; !ok; /W refs/heads/next \@gaa u4 DENIED by fallthru/ + gitolite access \@gaa u5 R any ; ok; /refs/heads/dev/; !/DENIED/ + gitolite access aa u5 R any ; ok; /refs/heads/dev/; !/DENIED/ + gitolite access \@gaa u5 + refs/heads/dev ; ok; /refs/heads/dev/; !/DENIED/ + gitolite access \@gaa u5 + refs/heads/master ; !ok; /\\+ refs/heads/master \@gaa u5 DENIED by refs/heads/master/ + gitolite access aa u5 + refs/heads/next ; !ok; /\\+ refs/heads/next aa u5 DENIED by fallthru/ + gitolite access \@gaa u5 R refs/heads/dev ; ok; /refs/heads/dev/; !/DENIED/ + gitolite access \@gaa u5 R refs/heads/master ; !ok; /R refs/heads/master \@gaa u5 DENIED by refs/heads/master/ + gitolite access \@gaa u5 R refs/heads/next ; ok; /refs/.*/; !/DENIED/ + gitolite access aa u5 W refs/heads/dev ; ok; /refs/heads/dev/; !/DENIED/ + gitolite access aa u5 W refs/heads/master ; !ok; /W refs/heads/master aa u5 DENIED by refs/heads/master/ + gitolite access \@gaa u5 W refs/heads/next ; ok; /refs/.*/; !/DENIED/ +"; + +confreset;confadd ' + @admins = admin dev1 + repo gitolite-admin + RW+ = admin + + @gr1 = r1 + repo @gr1 + RW refs/heads/v[0-9] = u1 + RW refs/heads = tester + + @gr2 = r2 + repo @gr2 + RW refs/heads/v[0-9] = u1 + - refs/heads/v[0-9] = tester + RW refs/heads = tester +'; + +try "ADMIN_PUSH set3; !/FATAL/" or die text(); + +try " + gitolite access \@gr2 tester W refs/heads/v1; !ok; /W refs/heads/v1 \@gr2 tester DENIED by refs/heads/v\\[0-9\\]/ + gitolite access \@gr1 tester W refs/heads/v1; ok; /refs/heads/; !/DENIED/ + gitolite access r1 tester W refs/heads/v1; ok; /refs/heads/; !/DENIED/ + gitolite access r2 tester W refs/heads/v1; !ok; /W refs/heads/v1 r2 tester DENIED by refs/heads/v\\[0-9\\]/ + gitolite access r2 tester W refs/heads/va; ok; /refs/heads/; !/DENIED/ +"; + From 446bd19de733e2abeee91d6357f77b8a13a678c9 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 10:40:27 +0530 Subject: [PATCH 542/850] tsh/test learn the cmp() function to make full output compares easier --- src/Gitolite/Test.pm | 2 ++ src/Gitolite/Test/Tsh.pm | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Gitolite/Test.pm b/src/Gitolite/Test.pm index df4cc89..5654477 100644 --- a/src/Gitolite/Test.pm +++ b/src/Gitolite/Test.pm @@ -11,6 +11,7 @@ package Gitolite::Test; dump confreset confadd + cmp ); #>>> use Exporter 'import'; @@ -24,6 +25,7 @@ BEGIN { *{'try'} = \&Tsh::try; *{'put'} = \&Tsh::put; *{'text'} = \&Tsh::text; + *{'cmp'} = \&Tsh::cmp; } use strict; diff --git a/src/Gitolite/Test/Tsh.pm b/src/Gitolite/Test/Tsh.pm index 41b4d12..491ee59 100644 --- a/src/Gitolite/Test/Tsh.pm +++ b/src/Gitolite/Test/Tsh.pm @@ -17,7 +17,7 @@ package Tsh; use Exporter 'import'; @EXPORT = qw( - try run AUTOLOAD + try run cmp AUTOLOAD rc error_count text lines error_list put cd tsh_tempdir @@ -470,6 +470,19 @@ sub fail { exit( $rc || 74 ); } +sub cmp { + # compare input string with text() + my $text = text(); + my $in = shift; + + if ($text eq $in) { + ok(); + } else { + fail('cmp failed', ''); + dbg(4, "\n\ntext = <<<$text>>>, in = <<<$in>>>\n\n"); + } +} + sub expect { my ( $patt, $msg ) = @_; $msg =~ s/^\s+// if $msg; From 8b8d3ef484aee01e73134ae3a6ce7ac4865bd27c Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 10:40:50 +0530 Subject: [PATCH 543/850] new test 'listers' --- t/listers.t | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100755 t/listers.t diff --git a/t/listers.t b/t/listers.t new file mode 100755 index 0000000..84add2f --- /dev/null +++ b/t/listers.t @@ -0,0 +1,147 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; + +try 'plan 30'; + +try "## info"; + +confreset;confadd ' + @oss = git gitolite gitolite3 + @prop = cc p4 + @crypto = alice bob carol + @dilbert = alice wally ashok + + repo @oss + RW = u1 @crypto + R = u2 @dilbert + repo @prop + RW = u2 @dilbert + R = u1 + repo t3 + RW = u3 + R = u4 +'; + +try "ADMIN_PUSH info; !/FATAL/" or die text(); +try " + /Initialized.*empty.*cc.git/ + /Initialized.*empty.*p4.git/ + /Initialized.*empty.*git.git/ + /Initialized.*empty.*gitolite.git/ + /Initialized.*empty.*gitolite3.git/ + /Initialized.*empty.*t3.git/ +"; + +try "gitolite list-groups"; cmp +'@crypto +@dilbert +@oss +@prop +'; + +try "gitolite list-users"; cmp +'@all +@crypto +@dilbert +admin +u1 +u2 +u3 +u4 +'; +try "gitolite list-repos"; cmp +'@oss +@prop +gitolite-admin +t3 +testing +'; + +try "gitolite list-phy-repos"; cmp +'cc +git +gitolite +gitolite-admin +gitolite3 +p4 +t3 +testing +'; + +try "gitolite list-memberships alice"; cmp +'@all +@crypto +@dilbert +alice +'; + +try "gitolite list-memberships ashok"; cmp +'@all +@dilbert +ashok +'; + +try "gitolite list-memberships carol"; cmp +'@all +@crypto +carol +'; + +try "gitolite list-memberships git"; cmp +'@all +@oss +git +'; + +try "gitolite list-memberships gitolite"; cmp +'@all +@oss +gitolite +'; + +try "gitolite list-memberships gitolite3"; cmp +'@all +@oss +gitolite3 +'; + +try "gitolite list-memberships cc"; cmp +'@all +@prop +cc +'; + +try "gitolite list-memberships p4"; cmp +'@all +@prop +p4 +'; + +try "gitolite list-members \@crypto"; cmp +'alice +bob +carol +'; + +try "gitolite list-members \@dilbert"; cmp +'alice +ashok +wally +'; + +try "gitolite list-members \@oss"; cmp +'git +gitolite +gitolite3 +'; + +try "gitolite list-members \@prop"; cmp +'cc +p4 +'; + From 876f6517f5a9605ab3ebdfedbe2e2ed93a242acf Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 15:05:51 +0530 Subject: [PATCH 544/850] (testing help) allow a *testing* rc to override the normal one --- src/Gitolite/Rc.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Gitolite/Rc.pm b/src/Gitolite/Rc.pm index 2a51a55..139d6e3 100644 --- a/src/Gitolite/Rc.pm +++ b/src/Gitolite/Rc.pm @@ -52,6 +52,10 @@ _die "$rc seems to be for older gitolite" if defined($GL_ADMINDIR); # let values specified in rc file override our internal ones @rc{ keys %RC } = values %RC; + # testing sometimes requires all of it to be overridden silently; use an + # env var that is highly unlikely to appear in real life :) + do $ENV{G3T_RC} if exists $ENV{G3T_RC} and -r $ENV{G3T_RC}; + # fix PATH (TODO: do it only if 'gitolite' isn't in PATH) $ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}"; From 9780ddab9de2856e005e699671606a78bc45ba8b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 16:00:15 +0530 Subject: [PATCH 545/850] (!!) personal branches -- 1 line of code, 50 lines of test! (and by the way even in g2 this was not so easy as just ONE line of code!) --- src/Gitolite/Conf/Load.pm | 2 +- t/personal-branches.t | 51 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100755 t/personal-branches.t diff --git a/src/Gitolite/Conf/Load.pm b/src/Gitolite/Conf/Load.pm index 9b38880..3d73ca7 100644 --- a/src/Gitolite/Conf/Load.pm +++ b/src/Gitolite/Conf/Load.pm @@ -68,7 +68,7 @@ sub access { trace( 3, scalar(@rules) . " rules found" ); for my $r (@rules) { my $perm = $r->[1]; - my $refex = $r->[2]; + my $refex = $r->[2]; $refex =~ s(/USER/)(/$user/); trace( 4, "perm=$perm, refex=$refex" ); # skip 'deny' rules if the ref is not (yet) known diff --git a/t/personal-branches.t b/t/personal-branches.t new file mode 100755 index 0000000..04e911f --- /dev/null +++ b/t/personal-branches.t @@ -0,0 +1,51 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; + +# basic tests +# ---------------------------------------------------------------------- + +try "plan 39"; + +confreset;confadd ' + @admins = admin dev1 + repo gitolite-admin + RW+ = admin + + repo testing + RW+ = @all + + @g1 = t1 + repo @g1 + R = u2 + RW = u3 + RW+ = u4 + RW a/USER/ = @all + RW+ p/USER/ = u1 u6 +'; + +try "ADMIN_PUSH set1; !/FATAL/" or die text(); + +try " + + gitolite access t1 u1; ok; /refs/heads/p/u1//; !/DENIED/ + gitolite access t1 u5; !ok; /\\+ any t1 u5 DENIED by fallthru/ + gitolite access \@g1 u5 W; ok; /refs/heads/a/u5//; !/DENIED/ + + gitolite access t1 u1 W refs/heads/a/user1/foo; !ok; /W refs/heads/a/user1/foo t1 u1 DENIED by fallthru/ + gitolite access \@g1 u1 + refs/heads/a/user1/foo; !ok; /\\+ refs/heads/a/user1/foo \@g1 u1 DENIED by fallthru/ + gitolite access t1 u1 W refs/heads/p/user1/foo; !ok; /W refs/heads/p/user1/foo t1 u1 DENIED by fallthru/ + gitolite access \@g1 u1 + refs/heads/p/user1/foo; !ok; /\\+ refs/heads/p/user1/foo \@g1 u1 DENIED by fallthru/ + + gitolite access \@g1 u1 W refs/heads/a/u1/foo; ok; /refs/heads/a/u1//; !/DENIED/ + gitolite access t1 u1 + refs/heads/a/u1/foo; !ok; /\\+ refs/heads/a/u1/foo t1 u1 DENIED by fallthru/ + gitolite access \@g1 u1 W refs/heads/p/u1/foo; ok; /refs/heads/p/u1//; !/DENIED/ + gitolite access t1 u1 + refs/heads/p/u1/foo; ok; /refs/heads/p/u1//; !/DENIED/ + + gitolite access \@g1 u1 W refs/heads/p/u2/foo; !ok; /W refs/heads/p/u2/foo \@g1 u1 DENIED by fallthru/ + gitolite access t1 u1 + refs/heads/p/u2/foo; !ok; /\\+ refs/heads/p/u2/foo t1 u1 DENIED by fallthru/ +"; From 5e2563bb8cdc534e520fa07c540a6e40019d163b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 15 Mar 2012 18:16:16 +0530 Subject: [PATCH 546/850] setup was over-engineered... --- src/Gitolite/Setup.pm | 60 +++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/src/Gitolite/Setup.pm b/src/Gitolite/Setup.pm index 09930bd..20143f0 100644 --- a/src/Gitolite/Setup.pm +++ b/src/Gitolite/Setup.pm @@ -4,20 +4,12 @@ package Gitolite::Setup; # ---------------------------------------------------------------------- =for args -Usage: gitolite setup [] +Usage: gitolite setup [