]> Lady’s Gitweb - Gitweb/blobdiff - gitweb.perl
gitweb: Add a per-repository authorization hook.
[Gitweb] / gitweb.perl
index 28b4c4b1e2bf04fe3f40d448d966a7fdfa79bb8211fb2b516fe4cdb0c2c22744..144d7ee2a56b12aeba816f491c483f447ee4174ec67d1c711eca705341bc27c2 100755 (executable)
@@ -96,6 +96,11 @@ our $default_projects_order = "project";
 # (only effective if this variable evaluates to true)
 our $export_ok = "++GITWEB_EXPORT_OK++";
 
 # (only effective if this variable evaluates to true)
 our $export_ok = "++GITWEB_EXPORT_OK++";
 
+# show repository only if this subroutine returns true
+# when given the path to the project, for example:
+#    sub { return -e "$_[0]/git-daemon-export-ok"; }
+our $export_auth_hook = undef;
+
 # only allow viewing of repositories also shown on the overview page
 our $strict_export = "++GITWEB_STRICT_EXPORT++";
 
 # only allow viewing of repositories also shown on the overview page
 our $strict_export = "++GITWEB_STRICT_EXPORT++";
 
@@ -401,7 +406,8 @@ sub check_head_link {
 sub check_export_ok {
        my ($dir) = @_;
        return (check_head_link($dir) &&
 sub check_export_ok {
        my ($dir) = @_;
        return (check_head_link($dir) &&
-               (!$export_ok || -e "$dir/$export_ok"));
+               (!$export_ok || -e "$dir/$export_ok") &&
+               (!$export_auth_hook || $export_auth_hook->($dir)));
 }
 
 # process alternate names for backward compatibility
 }
 
 # process alternate names for backward compatibility
@@ -617,6 +623,45 @@ sub evaluate_path_info {
                        $input_params{'hash_parent'} ||= $parentrefname;
                }
        }
                        $input_params{'hash_parent'} ||= $parentrefname;
                }
        }
+
+       # for the snapshot action, we allow URLs in the form
+       # $project/snapshot/$hash.ext
+       # where .ext determines the snapshot and gets removed from the
+       # passed $refname to provide the $hash.
+       #
+       # To be able to tell that $refname includes the format extension, we
+       # require the following two conditions to be satisfied:
+       # - the hash input parameter MUST have been set from the $refname part
+       #   of the URL (i.e. they must be equal)
+       # - the snapshot format MUST NOT have been defined already (e.g. from
+       #   CGI parameter sf)
+       # It's also useless to try any matching unless $refname has a dot,
+       # so we check for that too
+       if (defined $input_params{'action'} &&
+               $input_params{'action'} eq 'snapshot' &&
+               defined $refname && index($refname, '.') != -1 &&
+               $refname eq $input_params{'hash'} &&
+               !defined $input_params{'snapshot_format'}) {
+               # We loop over the known snapshot formats, checking for
+               # extensions. Allowed extensions are both the defined suffix
+               # (which includes the initial dot already) and the snapshot
+               # format key itself, with a prepended dot
+               while (my ($fmt, %opt) = each %known_snapshot_formats) {
+                       my $hash = $refname;
+                       my $sfx;
+                       $hash =~ s/(\Q$opt{'suffix'}\E|\Q.$fmt\E)$//;
+                       next unless $sfx = $1;
+                       # a valid suffix was found, so set the snapshot format
+                       # and reset the hash parameter
+                       $input_params{'snapshot_format'} = $fmt;
+                       $input_params{'hash'} = $hash;
+                       # we also set the format suffix to the one requested
+                       # in the URL: this way a request for e.g. .tgz returns
+                       # a .tgz instead of a .tar.gz
+                       $known_snapshot_formats{$fmt}{'suffix'} = $sfx;
+                       last;
+               }
+       }
 }
 evaluate_path_info();
 
 }
 evaluate_path_info();
 
@@ -722,6 +767,10 @@ if (defined $searchtext) {
 our $git_dir;
 $git_dir = "$projectroot/$project" if $project;
 
 our $git_dir;
 $git_dir = "$projectroot/$project" if $project;
 
+# list of supported snapshot formats
+our @snapshot_fmts = gitweb_check_feature('snapshot');
+@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
+
 # dispatch
 if (!defined $action) {
        if (defined $hash) {
 # dispatch
 if (!defined $action) {
        if (defined $hash) {
@@ -769,6 +818,7 @@ sub href (%) {
                #   - action
                #   - hash_parent or hash_parent_base:/file_parent
                #   - hash or hash_base:/filename
                #   - action
                #   - hash_parent or hash_parent_base:/file_parent
                #   - hash or hash_base:/filename
+               #   - the snapshot_format as an appropriate suffix
 
                # When the script is the root DirectoryIndex for the domain,
                # $href here would be something like http://gitweb.example.com/
 
                # When the script is the root DirectoryIndex for the domain,
                # $href here would be something like http://gitweb.example.com/
@@ -780,6 +830,10 @@ sub href (%) {
                $href .= "/".esc_url($params{'project'}) if defined $params{'project'};
                delete $params{'project'};
 
                $href .= "/".esc_url($params{'project'}) if defined $params{'project'};
                delete $params{'project'};
 
+               # since we destructively absorb parameters, we keep this
+               # boolean that remembers if we're handling a snapshot
+               my $is_snapshot = $params{'action'} eq 'snapshot';
+
                # Summary just uses the project path URL, any other action is
                # added to the URL
                if (defined $params{'action'}) {
                # Summary just uses the project path URL, any other action is
                # added to the URL
                if (defined $params{'action'}) {
@@ -819,6 +873,18 @@ sub href (%) {
                        $href .= esc_url($params{'hash'});
                        delete $params{'hash'};
                }
                        $href .= esc_url($params{'hash'});
                        delete $params{'hash'};
                }
+
+               # If the action was a snapshot, we can absorb the
+               # snapshot_format parameter too
+               if ($is_snapshot) {
+                       my $fmt = $params{'snapshot_format'};
+                       # snapshot_format should always be defined when href()
+                       # is called, but just in case some code forgets, we
+                       # fall back to the default
+                       $fmt ||= $snapshot_fmts[0];
+                       $href .= $known_snapshot_formats{$fmt}{'suffix'};
+                       delete $params{'snapshot_format'};
+               }
        }
 
        # now encode the parameters explicitly
        }
 
        # now encode the parameters explicitly
@@ -854,8 +920,7 @@ sub validate_project {
        my $input = shift || return undef;
        if (!validate_pathname($input) ||
                !(-d "$projectroot/$input") ||
        my $input = shift || return undef;
        if (!validate_pathname($input) ||
                !(-d "$projectroot/$input") ||
-               !check_head_link("$projectroot/$input") ||
-               ($export_ok && !(-e "$projectroot/$input/$export_ok")) ||
+               !check_export_ok("$projectroot/$input") ||
                ($strict_export && !project_in_list($input))) {
                return undef;
        } else {
                ($strict_export && !project_in_list($input))) {
                return undef;
        } else {
@@ -1648,8 +1713,6 @@ sub format_diff_line {
 # linked.  Pass the hash of the tree/commit to snapshot.
 sub format_snapshot_links {
        my ($hash) = @_;
 # linked.  Pass the hash of the tree/commit to snapshot.
 sub format_snapshot_links {
        my ($hash) = @_;
-       my @snapshot_fmts = gitweb_check_feature('snapshot');
-       @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
        my $num_fmts = @snapshot_fmts;
        if ($num_fmts > 1) {
                # A parenthesized list of links bearing format names.
        my $num_fmts = @snapshot_fmts;
        if ($num_fmts > 1) {
                # A parenthesized list of links bearing format names.
@@ -4856,20 +4919,17 @@ sub git_tree {
 }
 
 sub git_snapshot {
 }
 
 sub git_snapshot {
-       my @supported_fmts = gitweb_check_feature('snapshot');
-       @supported_fmts = filter_snapshot_fmts(@supported_fmts);
-
        my $format = $input_params{'snapshot_format'};
        my $format = $input_params{'snapshot_format'};
-       if (!@supported_fmts) {
+       if (!@snapshot_fmts) {
                die_error(403, "Snapshots not allowed");
        }
        # default to first supported snapshot format
                die_error(403, "Snapshots not allowed");
        }
        # default to first supported snapshot format
-       $format ||= $supported_fmts[0];
+       $format ||= $snapshot_fmts[0];
        if ($format !~ m/^[a-z0-9]+$/) {
                die_error(400, "Invalid snapshot format parameter");
        } elsif (!exists($known_snapshot_formats{$format})) {
                die_error(400, "Unknown snapshot format");
        if ($format !~ m/^[a-z0-9]+$/) {
                die_error(400, "Invalid snapshot format parameter");
        } elsif (!exists($known_snapshot_formats{$format})) {
                die_error(400, "Unknown snapshot format");
-       } elsif (!grep($_ eq $format, @supported_fmts)) {
+       } elsif (!grep($_ eq $format, @snapshot_fmts)) {
                die_error(403, "Unsupported snapshot format");
        }
 
                die_error(403, "Unsupported snapshot format");
        }
 
This page took 0.229025 seconds and 4 git commands to generate.