From ff4ff7c79ee0f37e84471e93804a95074b8e84ae Mon Sep 17 00:00:00 2001 From: Joe Rayhawk Date: Sun, 22 May 2011 22:19:16 -0700 Subject: Attempt to support core.sharedRepository --- libpiny/lib/Piny/Config.pm | 4 +- libpiny/lib/Piny/Repo.pm | 209 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 171 insertions(+), 42 deletions(-) (limited to 'libpiny') diff --git a/libpiny/lib/Piny/Config.pm b/libpiny/lib/Piny/Config.pm index a1813d4..1da5631 100644 --- a/libpiny/lib/Piny/Config.pm +++ b/libpiny/lib/Piny/Config.pm @@ -49,8 +49,8 @@ subtype 'HttpsUrl' subtype 'RepoPermission' => as 'Str' - => where { $_ eq "0666" or $_ eq "0664" or $_ eq "0660" or $_ eq "0640" or $_ eq "group" or $_ eq "true" or $_ eq "false" or $_ eq "all" or $_ eq "everybody" or $_ eq "world"} - => message { 'Must be one of 0666, 0664 (or all, everybody, world), 0660 (or true, group), or 0640 (or false).' } + => where { $_ eq "0666" or $_ eq "0664" or $_ eq "0660" or $_ eq "0640" or $_ eq "group" or $_ eq "true" or $_ eq "false" or $_ eq "all" or $_ eq "everybody" or $_ eq "world" or $_ eq "1" or $_ eq "2" } + => message { 'Must be one of 0666, 0664 (or all, everybody, world), 0660 (or true, 1, group), or 0640 (or false, 0).' } ; # Attributes diff --git a/libpiny/lib/Piny/Repo.pm b/libpiny/lib/Piny/Repo.pm index 3bb275d..8c38323 100644 --- a/libpiny/lib/Piny/Repo.pm +++ b/libpiny/lib/Piny/Repo.pm @@ -174,7 +174,21 @@ has 'secure_path' => , init_arg => undef ); -has 'apache_config' => +has 'apache_global_config' => + ( is => 'ro' + , isa => 'Str' + , lazy_build => 1 + , init_arg => undef + ); + +has 'apache_secure_config' => + ( is => 'ro' + , isa => 'Str' + , lazy_build => 1 + , init_arg => undef + ); + +has 'apache_www_config' => ( is => 'ro' , isa => 'Str' , lazy_build => 1 @@ -224,6 +238,9 @@ sub rebuild { sub rebuild_git { my ( $s ) = @_; + my $dirperm; + my $fileperm; + unless( getgrnam("git-" . $s->shortname ) ) { system( "/usr/sbin/addgroup", "--quiet", "git-" . $s->shortname ) and die "Could not create repo group!"; system( "/usr/sbin/adduser", "--quiet", $s->owner->name, "git-" . $s->shortname ) and die "Could not add you to the repo group!"; @@ -237,20 +254,45 @@ sub rebuild_git { }; }; - chown( 0, 0, $s->path, $s->path . "/config" ) or die "Could not change ownership of git config!"; - foreach( "info", "logs", "branches" ) { (-e $s->path . "/" . $_) or mkdir( $s->path . "/" . $_ ) or die "Could not mkdir $_ for repo: $!"; }; - foreach( "branches", "description", "HEAD", "info", "logs", "objects", "packed-refs", "refs" ) { - system( "/bin/chown", "-R", $s->owner->name . "." . $s->group->name, $s->path . "/" . $_ ) and die "Could not change ownership of $_ for repo: $!"; + if ( $s->config->core_sharedrepository eq "0666" ) { + $dirperm = "2777"; + $fileperm = "0644"; + } elsif ( $s->config->core_sharedrepository =~ /^(0664|all|everybody|world)$/ ) { + $dirperm = "2775"; + $fileperm = "0644"; + } elsif ( $s->config->core_sharedrepository =~ /^(0660|true|1|group)$/ ) { + $dirperm = "2770"; + $fileperm = "0640"; + } elsif ( $s->config->core_sharedrepository =~ /^(0640|false|0)$/ ) { + $dirperm = "2750"; + $fileperm = "0640"; + } else { + die $s->config->core_sharedrepository . "is an unhandled value!" + }; + + # FIXME: we should verify we are not breaking someone else's object hardlinks with these chmod or chown operations + system( "/usr/bin/find " . $s->path . "/refs " . $s->path . "/info " . $s->path . "/branches " . $s->path . "/objects " . $s->path . "/logs " . $s->path . "/HEAD " . $s->path . "/packed-refs -type d -print0 | /usr/bin/xargs -0 /bin/chmod $dirperm" ) and die "Could not chmod shared git resources!"; + system( "/usr/bin/find " . $s->path . "/refs " . $s->path . "/info " . $s->path . "/branches " . $s->path . "/objects " . $s->path . "/logs " . $s->path . "/HEAD " . $s->path . "/packed-refs -type f -print0 | /usr/bin/xargs -0 /bin/chmod $fileperm" ) and die "Could not chmod shared git resources!"; # most files are either immutable or replaced at link level + system( "/usr/bin/find " . $s->path . "/refs " . $s->path . "/info " . $s->path . "/branches " . $s->path . "/objects " . $s->path . "/logs " . $s->path . "/HEAD " . $s->path . "/packed-refs -print0 | /usr/bin/xargs -0 /bin/chgrp " . $s->group->name ) and die "Could not chgrp shared git resources!"; + + chown( 0, 0, $s->path ) or die "Could not change ownership of " . $s->path; + chmod( 00755, $s->path ) or die "Could not change mode of " . $s->path; + + foreach( "config", "description", "git-daemon-export-ok" ) { + chown( 0, 0, $s->path . "/" . $_ ) or die "Could not change ownership of $_!"; + chmod( 00644, $s->path . "/" . $_ ) or die "Could not change mode of $_!"; }; $ENV{"GIT_DIR"} = $s->path; system( "/usr/bin/git", "config", "gitweb.owner", $s->owner->email->address ) and die "Could not git config gitweb.owner!"; # packed-refs files aren't modifiable under our permission system and are poorly supported on old version of git anyway. system( "/usr/bin/git", "config", "gc.packrefs", "0" ) and die "Could not git config gc.packrefs!"; + # git-upload-pack will default to core.sharedrepository 'false', so we make sure the pinyconfig value is set here. + system( "/usr/bin/git", "config", "core.sharedrepository", $s->config->core_sharedrepository ) and die "Could not git config core.sharedrepository!"; delete $ENV{"GIT_DIR"}; $s->clear_config; @@ -274,15 +316,16 @@ sub rebuild_ikiwiki { print SETUP $s->ikiwiki_setup; close( SETUP ) or die "Could not close new ikiwiki setup file: $!"; - unless( -d $s->ikiwiki_srcdir ) { - system( "/usr/bin/git", "clone", "--quiet", $s->path, $s->ikiwiki_srcdir ) and die "Could not clone repo to ikiwiki srcdir!"; + foreach( $s->ikiwiki_srcdir, $s->config->piny_ikiwikidestdir . $s->name, $s->ikiwiki_destdir, $s->secure_path ) { + unless( -d $_ ) { mkdir( $_ ) }; + system( "/bin/chown", "-R", $ikiuser->name . ".", $_ ) and die "Could not change ownership of ikiwiki directories!"; }; system( "/usr/bin/find " . $s->ikiwiki_srcdir . " -type d -name .ikiwiki -print0 | xargs -0 --no-run-if-empty rm -r") and die "Could not remove old Ikiwiki state dir!"; - foreach( $s->ikiwiki_srcdir, $s->ikiwiki_destdir, $s->secure_path ) { - unless( -d $_ ) { mkdir( $_ ) }; - system( "/bin/chown", "-R", $ikiuser->name . ".", $_ ) and die "Could not change ownership of ikiwiki directories!"; + unless( -d $s->ikiwiki_srcdir . ".git" ) { + # do this after the chown -R because there are a lot of object hardlinks flying around that don't want to chown + system( "/usr/bin/sudo", "-u", $ikiuser->name, "/usr/bin/git", "clone", "--quiet", $s->path, $s->ikiwiki_srcdir ) and die "Could not clone repo to ikiwiki srcdir!"; }; open( WIKILIST, ">", "/etc/ikiwiki/wikilist.d/" . $s->name ) or die "Could not create wikilist.d file: $!"; @@ -293,12 +336,23 @@ sub rebuild_ikiwiki { system( "/usr/bin/sudo", "-u", $ikiuser->name, "/usr/bin/ikiwiki", "--setup", "/etc/ikiwiki/piny/" . $s->name . ".setup" ) and die "Could not do initial compile of ikiwiki!"; - open( APACHE, ">", "/etc/apache2/piny-available/" . $s->name ) or die "Could not open new apache config: $!"; - print APACHE $s->apache_config; - close( APACHE ) or die "Could not close new apache config: $!"; + my $globaltemp = File::Temp->new( ) or die "Could not create temporary file: $!"; + $globaltemp->unlink_on_destroy( 0 ); + print $globaltemp $s->apache_global_config; + $globaltemp->close or die "Could not close new wikilist: $!"; + rename( $globaltemp->filename, "/etc/apache2/piny/global/" . $s->name ) or die "Could not rename apache config: $!"; + + my $securetemp = File::Temp->new( ) or die "Could not create temporary file: $!"; + $securetemp->unlink_on_destroy( 0 ); + print $securetemp $s->apache_secure_config; + $securetemp->close or die "Could not close new wikilist: $!"; + rename( $securetemp->filename, "/etc/apache2/piny/secure/" . $s->name ) or die "Could not rename apache config: $!"; - unlink( "/etc/apache2/piny-enabled/" . $s->name ); - symlink( "/etc/apache2/piny-available/" . $s->name, "/etc/apache2/piny-enabled/" . $s->name ) or die "Could not symlink apache config: $!"; + my $wwwtemp = File::Temp->new( ) or die "Could not create temporary file: $!"; + $wwwtemp->unlink_on_destroy( 0 ); + print $wwwtemp $s->apache_www_config; + $wwwtemp->close or die "Could not close new wikilist: $!"; + rename( $wwwtemp->filename, "/etc/apache2/piny/www/" . $s->name ) or die "Could not rename apache config: $!"; system( "/etc/init.d/apache2", "reload" ) and die "Could not reload apache config!"; }; @@ -340,7 +394,7 @@ sub destroy_ikiwiki { my $user = Piny::Environment->instance->user; - foreach( "/etc/apache2/piny-enabled/" . $s->name, "/etc/apache2/piny-available/" . $s->name, ) { + foreach( "/etc/apache2/piny/global/" . $s->name, "/etc/apache2/piny/secure/" . $s->name, "/etc/apache2/piny/www/" . $s->name, ) { if ( -e $_ ) { unlink( $_ ); }; @@ -353,7 +407,7 @@ sub destroy_ikiwiki { $s->rebuild_wikilist; }; - foreach( $s->secure_path, $s->ikiwiki_destdir, $s->ikiwiki_srcdir, "/etc/ikiwiki/piny/" . $s->name . ".setup" ) { + foreach( $s->secure_path, $s->config->piny_ikiwikidestdir . $s->name, $s->config->piny_ikiwikisecurepath . "read/" . $s->name, $s->ikiwiki_destdir, $s->ikiwiki_srcdir, "/etc/ikiwiki/piny/" . $s->name . ".setup" ) { if ( -e $_ ) { system( "rm", "-rf", $_ ); }; @@ -417,18 +471,19 @@ sub create { mkdir( $repo->path ) or die "The repo $name appears to already exist! ($!)"; - $ENV{"GIT_DIR"} = $repo->path; - system( "/usr/bin/git", "init", "--bare", "--quiet", "--shared" ) and die "Could not initialize git repo!"; - delete $ENV{"GIT_DIR"}; - - system( "/bin/chown", "-R", $user->name, $repo->path . "/objects" ) and die "Could not change ownership of $_ for repo: $!"; - + chdir( "/srv/git" ); # so git-clone can do the work of resolving local repo names for us if ( defined $source ) { - $ENV{"GIT_DIR"} = $source; - system( "/usr/bin/git", "push", "--mirror", $repo->path ) and die "Could not push refs to new repository!"; - delete $ENV{"GIT_DIR"}; + system( "/bin/chown", $user . "." . $user, $repo->path ) and die "Could not change ownership of repo path!"; # permissions are overridden later + if ( system( "/usr/bin/sudo", "-u", $user, "/usr/bin/git", "clone", "--bare", "--no-hardlinks", "--quiet", $source, $repo->path ) ) { # sudo -u $user needed in order to test user readability of whatever they want to clone. the mode o+rx should probably be masked off if we want private repos to stay private. + system( "/bin/rm", "-r", $repo->path ) and die "Failed to clean up after failed clone operation!"; + die( "Could not clone repository!\n" ); + }; + } else { + system( "/usr/bin/git", "init", "--bare", "--quiet", $repo->path ) and die "Could not initialize git repo!"; }; + system( "/bin/chown", "-R", $user->name, $repo->path . "/objects" ) and die "Could not change ownership of $_ for repo: $!"; + $repo->description( $description ); $repo->rebuild_git; @@ -544,7 +599,17 @@ sub _build_globally_writable { sub _build_cgit_url { my ( $s ) = @_; - return $s->config->piny_ikiwikisecureurl . "cgit/" . $s->name; + if ( $s->config->core_sharedrepository eq "0666" ) { + return $s->config->piny_ikiwikisecureurl . "cgit/" . $s->name; + } elsif ( $s->config->core_sharedrepository =~ /^(0664|all|everybody|world)$/ ) { + return $s->config->piny_ikiwikisecureurl . "cgit/" . $s->name; + } elsif ( $s->config->core_sharedrepository =~ /^(0660|true|1|group)$/ ) { + return $s->config->piny_ikiwikisecureurl . "auth/cgit/" . $s->name; + } elsif ( $s->config->core_sharedrepository =~ /^(0640|false|0)$/ ) { + return $s->config->piny_ikiwikisecureurl . "auth/cgit/" . $s->name; + } else { + die ( $s->config->core_sharedrepository . "is an unhandled value!" ); + }; } sub _build_ikiwiki_setup { @@ -593,7 +658,17 @@ sub _build_ikiwiki_setup { sub _build_ikiwiki_destdir { my ( $s ) = @_; - return $s->config->piny_ikiwikidestdir . $s->name; + if ( $s->config->core_sharedrepository eq "0666" ) { + return ( $s->config->piny_ikiwikidestdir . $s->name ); + } elsif ( $s->config->core_sharedrepository =~ /^(0664|all|everybody|world)$/ ) { + return ( $s->config->piny_ikiwikidestdir . $s->name ); + } elsif ( $s->config->core_sharedrepository =~ /^(0660|true|1|group)$/ ) { + return ( $s->config->piny_ikiwikisecurepath . "read/" . $s->name ); + } elsif ( $s->config->core_sharedrepository =~ /^(0640|false|0)$/ ) { + return ( $s->config->piny_ikiwikisecurepath . "read/" . $s->name ); + } else { + die ( $s->config->core_sharedrepository . "is an unhandled value!" ); + }; }; sub _build_ikiwiki_srcdir { @@ -605,19 +680,29 @@ sub _build_ikiwiki_srcdir { sub _build_ikiwiki_url { my ( $s ) = @_; - return $s->config->piny_ikiwikiurl . $s->name; + if ( $s->config->core_sharedrepository eq "0666" ) { + return ( $s->config->piny_ikiwikiurl . $s->name ); + } elsif ( $s->config->core_sharedrepository =~ /^(0664|all|everybody|world)$/ ) { + return ( $s->config->piny_ikiwikiurl . $s->name ); + } elsif ( $s->config->core_sharedrepository =~ /^(0660|true|1|group)$/ ) { + return ( $s->config->piny_ikiwikisecureurl . "read/" . $s->name ); + } elsif ( $s->config->core_sharedrepository =~ /^(0640|false|0)$/ ) { + return ( $s->config->piny_ikiwikisecureurl . "read/" . $s->name ); + } else { + die ( $s->config->core_sharedrepository . "is an unhandled value!" ); + }; }; sub _build_ikiwiki_cgiurl { my ( $s ) = @_; - return $s->config->piny_ikiwikisecureurl . "repos/" . $s->name . "/ikiwiki.cgi"; + return $s->config->piny_ikiwikisecureurl . "write/" . $s->name . "/ikiwiki.cgi"; }; sub _build_secure_path { my ( $s ) = @_; - return $s->config->piny_ikiwikisecurepath . "repos/" . $s->name; + return $s->config->piny_ikiwikisecurepath . "write/" . $s->name; }; sub _build_ikiwiki_cgipath { @@ -629,27 +714,71 @@ sub _build_ikiwiki_cgipath { sub _build_ikiwiki_historyurl { my ( $s ) = @_; - if ( defined $s->config->{"https_url"} ) { - return $s->config->{"https_url"} . "cgit/" . $s->name . "/log/[[file]]"; + return $s->cgit_url . "/log/[[file]]"; +}; + +sub _build_ikiwiki_diffurl { + my ( $s ) = @_; + + return $s->cgit_url . "/log/[[file]]"; +}; + +sub _build_apache_global_config { + my ( $s ) = @_; + + if ( $s->config->core_sharedrepository eq "0666" ) { + return ( + "secure_path . ">\n SSLRequireSSL\n AuthPAM_Enabled on\n" . " AuthPAM_FallThrough off\n AuthBasicAuthoritative off\n AuthType Basic\n AuthName \"Valid Piny user needed.\"\n" . " Require valid-user\n" . " \n" + ); + } elsif ( $s->config->core_sharedrepository =~ /^(0664|all|everybody|world)$/ ) { + return ( + "secure_path . ">\n SSLRequireSSL\n AuthPAM_Enabled on\n AuthGROUP_Enabled on\n AuthPAM_FallThrough off\n AuthBasicAuthoritative off\n AuthType Basic\n AuthName \"User with access to " . $s->name . " repository needed.\"\n Require group " . $s->group->name . "\n\n" + ); + } elsif ( $s->config->core_sharedrepository =~ /^(0660|true|1|group)$/ ) { + return ( + "secure_path . ">\n SSLRequireSSL\n AuthPAM_Enabled on\n AuthGROUP_Enabled on\n AuthPAM_FallThrough off\n AuthBasicAuthoritative off\n AuthType Basic\n AuthName \"User with access to " . $s->name . " repository needed.\"\n Require group " . $s->group->name . "\n\n" . + "ikiwiki_destdir . ">\n SSLRequireSSL\n AuthPAM_Enabled on\n AuthGROUP_Enabled on\n AuthPAM_FallThrough off\n AuthBasicAuthoritative off\n AuthType Basic\n AuthName \"User with access to " . $s->name . " repository needed.\"\n Require group " . $s->group->name . "\n\n" + ); + } elsif ( $s->config->core_sharedrepository =~ /^(0640|false|0)$/ ) { + return ( + "secure_path . ">\n SSLRequireSSL\n AuthPAM_Enabled on\n" . " AuthPAM_FallThrough off\n AuthBasicAuthoritative off\n AuthType Basic\n AuthName \"Owner of " . $s->name . " repository needed.\"\n Require user " . $s->owner->name . "\n\n" . + "ikiwiki_destdir . ">\n SSLRequireSSL\n AuthPAM_Enabled on\n AuthGROUP_Enabled on\n AuthPAM_FallThrough off\n AuthBasicAuthoritative off\n AuthType Basic\n AuthName \"User with access to " . $s->name . " repository needed.\"\n Require group " . $s->group->name . "\n\n" + ); } else { - return $s->config->piny_ikiwikisecureurl . "cgit/" . $s->name . "/log/[[file]]"; + die ( $s->config->core_sharedrepository . "is an unhandled value!" ); }; }; -sub _build_ikiwiki_diffurl { +sub _build_apache_secure_config { my ( $s ) = @_; - if ( defined $s->config->{"https_url"} ) { - return $s->config->{"https_url"} . "cgit/" . $s->name . "/diff/?id=[[sha1_commit]]"; + if ( $s->config->core_sharedrepository eq "0666" ) { + return ( "Redirect /read/" . $s->name . " " . $s->ikiwiki_url . "\n" ); + } elsif ( $s->config->core_sharedrepository =~ /^(0664|all|everybody|world)$/ ) { + return ( "Redirect /read/" . $s->name . " " . $s->ikiwiki_url . "\n" ); + } elsif ( $s->config->core_sharedrepository =~ /^(0660|true|1|group)$/ ) { + return ( "Redirect /cgit/" . $s->name . " /auth/cgit/" . $s->name . "\n" ); + } elsif ( $s->config->core_sharedrepository =~ /^(0640|false|0)$/ ) { + return ( "Redirect /cgit/" . $s->name . " /auth/cgit/" . $s->name . "\n" ); } else { - return $s->config->piny_ikiwikisecureurl . "cgit/" . $s->name . "/diff/?id=[[sha1_commit]]"; + die ( $s->config->core_sharedrepository . "is an unhandled value!" ); }; }; -sub _build_apache_config { +sub _build_apache_www_config { my ( $s ) = @_; - return "secure_path . ">\n AuthPAM_Enabled on\n AuthGROUP_Enabled on\n AuthPAM_FallThrough off\n AuthBasicAuthoritative off\n AuthType Basic\n AuthName \"User access to " . $s->name . " repository needed.\"\n Require group " . $s->group->name . "\n\n"; + if ( $s->config->core_sharedrepository eq "0666" ) { + return ( "\n" ); + } elsif ( $s->config->core_sharedrepository =~ /^(0664|all|everybody|world)$/ ) { + return ( "\n" ); + } elsif ( $s->config->core_sharedrepository =~ /^(0660|true|1|group)$/ ) { + return ( "Redirect /" . $s->name . " " . $s->ikiwiki_url . "\n" ); + } elsif ( $s->config->core_sharedrepository =~ /^(0640|false|0)$/ ) { + return ( "Redirect /" . $s->name . " " . $s->ikiwiki_url . "\n" ); + } else { + die ( $s->config->core_sharedrepository . "is an unhandled value!" ); + }; }; sub _build_config { -- cgit v1.2.3