po: Add failing test for Debian bug #911356
[ikiwiki] / t / git-untrusted.t
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4
5 use File::Temp;
6 use Test::More;
7
8 BEGIN {
9         my $git = `which git`;
10         chomp $git;
11         plan(skip_all => 'git not available') unless -x $git;
12
13         plan(skip_all => "IPC::Run not available")
14                 unless eval q{
15                         use IPC::Run qw(run start);
16                         1;
17                 };
18
19         use_ok('IkiWiki');
20         use_ok('YAML::XS');
21 }
22
23 # We check for English error messages
24 $ENV{LC_ALL} = 'C';
25
26 use Cwd qw(getcwd);
27 use Errno qw(ENOENT);
28
29 my $installed = $ENV{INSTALLED_TESTS};
30 my $tmp = File::Temp->newdir(CLEANUP => 0);
31
32 my @command;
33 if ($installed) {
34         @command = qw(ikiwiki);
35 }
36 else {
37         ok(! system("make -s ikiwiki.out"));
38         @command = ("perl", "-I".getcwd."/blib/lib", './ikiwiki.out',
39                 '--underlaydir='.getcwd.'/underlays/basewiki',
40                 '--set', 'underlaydirbase='.getcwd.'/underlays',
41                 '--templatedir='.getcwd.'/templates');
42 }
43
44 sub write_old_file {
45         my $name = shift;
46         my $dir = shift;
47         my $content = shift;
48         writefile($name, $dir, $content);
49         ok(utime(333333333, 333333333, "$dir/$name"));
50 }
51
52 sub write_setup_file {
53         my %params = @_;
54         my %setup = (
55                 wikiname => 'this is the name of my wiki',
56                 srcdir => "$tmp/srcdir",
57                 destdir => "$tmp/out",
58                 url => 'http://example.com',
59                 cgiurl => 'http://example.com/cgi-bin/ikiwiki.cgi',
60                 cgi_wrapper => "$tmp/ikiwiki.cgi",
61                 cgi_wrappermode => '0755',
62                 add_plugins => [qw(anonok attachment lockedit recentchanges)],
63                 disable_plugins => [qw(emailauth openid passwordauth)],
64                 anonok_pagespec => 'writable/*',
65                 locked_pages => '!writable/*',
66                 rcs => 'git',
67                 git_wrapper => "$tmp/repo.git/hooks/post-update",
68                 git_wrappermode => '0755',
69                 gitorigin_branch => 'test-repo',
70                 gitmaster_branch => 'master',
71                 untrusted_committers => [$params{trustme} ? 'nobody' : scalar getpwuid($<)],
72                 git_test_receive_wrapper => "$tmp/repo.git/hooks/pre-receive",
73                 ENV => { LC_ALL => 'C' },
74                 verbose => 1,
75         );
76         unless ($installed) {
77                 $setup{ENV}{'PERL5LIB'} = getcwd.'/blib/lib';
78         }
79         writefile("test.setup", "$tmp",
80                 "# IkiWiki::Setup::Yaml - YAML formatted setup file\n" .
81                 Dump(\%setup));
82 }
83
84 sub thoroughly_rebuild {
85         ok(unlink("$tmp/ikiwiki.cgi") || $!{ENOENT});
86         ok(unlink("$tmp/repo.git/hooks/post-update") || $!{ENOENT});
87         ok(unlink("$tmp/repo.git/hooks/pre-receive") || $!{ENOENT});
88         ok(! system(@command, qw(--setup), "$tmp/test.setup", qw(--rebuild --wrappers)));
89 }
90
91 sub try_run_git {
92         my $args = shift;
93         my %params = @_;
94         my $git_dir = $params{chdir} || "$tmp/srcdir";
95         my ($in, $out, $err);
96         my @redirections = ('>', \$in);
97         if ($params{capture_stdout}) {
98                 push @redirections, '>', \$out;
99         }
100         else {
101                 push @redirections, '>', \*STDERR;
102         }
103         push @redirections, '2>', \$err if $params{capture_stderr};
104         my $h = start(['git', @$args], @redirections, init => sub {
105                 chdir $git_dir or die $!;
106                 my $name = 'The IkiWiki Tests';
107                 my $email = 'nobody@ikiwiki-tests.invalid';
108                 if ($args->[0] eq 'commit') {
109                         $ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $name;
110                         $ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} = $email;
111                 }
112         });
113         while ($h->pump) {};
114         $h->finish;
115         return $h, $out, $err;
116 }
117
118 sub run_git {
119         my (undef, $filename, $line) = caller;
120         my $args = shift;
121         my %params = @_;
122         my $git_dir = $params{chdir} || "$tmp/srcdir";
123         my $desc = $params{desc} || join(' ', 'git', @$args);
124         my ($h, $out, $err) = try_run_git($args, capture_stdout => 1, %params);
125         is($h->full_result(0), 0, "'$desc' in $git_dir at $filename:$line");
126         return $out;
127 }
128
129 sub test {
130         my ($h, $out, $err);
131         my $sha1;
132
133         write_old_file('.gitignore', "$tmp/srcdir", "/.ikiwiki/\n");
134         write_old_file('writable/one.mdwn', "$tmp/srcdir", 'This is the first test page');
135         write_old_file('writable/two.bin', "$tmp/srcdir", 'An attachment');
136
137         unless ($installed) {
138                 ok(! system(qw(cp -pRL doc/wikiicons), "$tmp/srcdir/"));
139                 ok(! system(qw(cp -pRL doc/recentchanges.mdwn), "$tmp/srcdir/"));
140         }
141
142         ok(mkdir "$tmp/repo.git");
143         run_git(['init', '--bare'], chdir => "$tmp/repo.git");
144
145         run_git(['init']);
146         run_git(['add', '.']);
147         run_git(['commit', '-m', 'Initial commit']);
148         my $initial_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
149         run_git(['remote', 'add', 'test-repo', "$tmp/repo.git"]);
150         run_git(['push', 'test-repo', 'master:master']);
151         run_git(['branch', '--set-upstream-to=test-repo/master']);
152
153         run_git(['clone', '-otest-repo', "$tmp/repo.git", "$tmp/clone"], chdir => $tmp);
154         writefile('writable/untrusted_user_says_hi.mdwn', "$tmp/clone", 'Hi!');
155         run_git(['add', 'writable'], chdir => "$tmp/clone");
156         run_git(['commit', '-m', 'Hi'], chdir => "$tmp/clone");
157         my $allowed_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD'],
158                 chdir => "$tmp/clone");
159
160         diag 'Pushing unrestricted change as untrusted user';
161         write_setup_file(trustme => 0);
162         thoroughly_rebuild();
163
164         $out = run_git([
165                 'push', 'test-repo', 'master:master',
166         ], chdir => "$tmp/clone");
167         $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD'], chdir => "$tmp/repo.git");
168         is($sha1, $allowed_sha1, 'allowed commit was pushed');
169         $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
170         is($sha1, $allowed_sha1, 'allowed commit was pushed');
171         ok(-e "$tmp/srcdir/writable/untrusted_user_says_hi.mdwn");
172         ok(-e "$tmp/out/writable/untrusted_user_says_hi/index.html");
173
174         diag 'Pushing restricted change as untrusted user';
175         writefile('staff_only.mdwn', "$tmp/clone", 'Hi!');
176         run_git(['add', 'staff_only.mdwn'], chdir => "$tmp/clone");
177         run_git(['commit', '-m', 'Hi'], chdir => "$tmp/clone");
178         my $proposed_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD'],
179                 chdir => "$tmp/clone");
180
181         ($h, $out, $err) = try_run_git([
182                 'push', 'test-repo', 'master:master',
183         ], chdir => "$tmp/clone", capture_stdout => 1, capture_stderr => 1);
184         isnt($h->full_result(0), 0);
185         is($out, '');
186         like($err, qr{remote: <.*>staff only</.*> is locked and cannot be edited});
187         $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD'], chdir => "$tmp/repo.git");
188         is($sha1, $allowed_sha1, 'proposed commit was not pushed');
189         $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
190         is($sha1, $allowed_sha1, 'proposed commit was not pushed');
191         ok(! -e "$tmp/srcdir/staff_only.mdwn");
192         ok(! -e "$tmp/out/staff_only/index.html");
193
194         diag 'Pushing restricted change as trusted user';
195         write_setup_file(trustme => 1);
196         thoroughly_rebuild();
197
198         $out = run_git([
199                 'push', 'test-repo', 'master:master',
200         ], chdir => "$tmp/clone");
201         $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD'], chdir => "$tmp/repo.git");
202         is($sha1, $proposed_sha1, 'proposed commit was pushed');
203         $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
204         is($sha1, $proposed_sha1, 'proposed commit was pushed');
205         ok(-e "$tmp/srcdir/staff_only.mdwn");
206         ok(-e "$tmp/out/staff_only/index.html");
207 }
208
209 test();
210
211 done_testing();