diff options
-rwxr-xr-x | git/gitaclhook | 371 |
1 files changed, 0 insertions, 371 deletions
diff --git a/git/gitaclhook b/git/gitaclhook deleted file mode 100755 index dd31dd8..0000000 --- a/git/gitaclhook +++ /dev/null | |||
@@ -1,371 +0,0 @@ | |||
1 | #! /usr/bin/perl | ||
2 | # Copyright (C) 2013 Sergey Poznyakoff <gray@gnu.org> | ||
3 | # | ||
4 | # This program is free software; you can redistribute it and/or modify | ||
5 | # it under the terms of the GNU General Public License as published by | ||
6 | # the Free Software Foundation; either version 3, or (at your option) | ||
7 | # any later version. | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, | ||
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | # GNU General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License | ||
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | |||
17 | use strict; | ||
18 | use File::Spec; | ||
19 | use Pod::Man; | ||
20 | use Pod::Usage; | ||
21 | |||
22 | =head1 NAME | ||
23 | |||
24 | gitaclhook - control access to git repositories | ||
25 | |||
26 | =head1 SYNOPSIS | ||
27 | |||
28 | B<gitaclhook> I<refname> I<old-sha1> I<new-sha1> | ||
29 | |||
30 | B<gitaclhook --help> | ||
31 | |||
32 | =head1 DESCRIPTION | ||
33 | |||
34 | This program is intended to be run as an "update" hook by git. | ||
35 | It is called by B<git-receive-pack> with arguments: | ||
36 | I<refname> I<old-sha1> I<new-sha1>. | ||
37 | |||
38 | If the B<hooks.aclfile> keyword is defined in the repository's config file, | ||
39 | this hook will parse the file and allow or deny update depending on | ||
40 | its settings. If B<hooks.aclfile> is not defined, update is allowed | ||
41 | unconditionally. | ||
42 | |||
43 | =head1 ACL FILE | ||
44 | |||
45 | The ACL file has the usual line-oriented syntax. Comments are introduced | ||
46 | by the # sign and extend to the end of the physical line. Comments and | ||
47 | empty lines are ignored. | ||
48 | |||
49 | Non-empty lines introduce ACL rules. The syntax is: | ||
50 | |||
51 | =over 4 | ||
52 | |||
53 | I<VERB> I<PROJECT> I<USER> [I<OP> I<REF>] | ||
54 | |||
55 | =back | ||
56 | |||
57 | where brackets denote optional parts. The parts of an ACL are: | ||
58 | |||
59 | =over 4 | ||
60 | |||
61 | =item I<VERB> | ||
62 | |||
63 | Either B<allow> or B<deny>, to allow or deny the operation, correspondingly. | ||
64 | |||
65 | =item I<PROJECT> | ||
66 | |||
67 | The name of the project. It is obtained by removing the directory | ||
68 | and suffix parts of the repository pathname. Thus, if the repository | ||
69 | is located in B</var/gitroot/foobar.git>, then the corresponding name of | ||
70 | the project is B<foobar>. | ||
71 | |||
72 | An asterisk matches any project name. | ||
73 | |||
74 | =item I<USER> | ||
75 | |||
76 | Name of the user. The word B<all> stands for any user, the word B<none> | ||
77 | matches no one at all. Otherwise, if this part begins with a percent | ||
78 | sign (B<%>), the rest of characters are treated as the name of the UNIX | ||
79 | group to check and the rule matches any user in that group. Otherwise, | ||
80 | the literal match is assumed. | ||
81 | |||
82 | =back | ||
83 | |||
84 | The optional parts are: | ||
85 | |||
86 | =over 4 | ||
87 | |||
88 | =item I<OP> | ||
89 | |||
90 | Requested operation codes. It is a string consisting of one or more | ||
91 | of the following letters (case-insensitive): | ||
92 | |||
93 | =over 8 | ||
94 | |||
95 | =item B<C> | ||
96 | |||
97 | Create new ref. | ||
98 | |||
99 | =item B<D> | ||
100 | |||
101 | Delete existing ref. | ||
102 | |||
103 | =item B<U> | ||
104 | |||
105 | Fast-forward existing ref (no commit loss). | ||
106 | |||
107 | =item B<R> | ||
108 | |||
109 | Rewind or rebase existing ref (commit loss). | ||
110 | |||
111 | =back | ||
112 | |||
113 | =item I<REF> | ||
114 | |||
115 | Affected ref, relative to the git B<refs/> directory. If it begins with | ||
116 | a caret (B<^>), it is treated as a Perl regular expression (with the B<^> | ||
117 | being its part). If it ends with a B</>, it is treated as a prefix match, | ||
118 | so, e.g., B<heads/baz/> matches B<refs/heads/baz> and anything below. | ||
119 | Otherwise, it must match exactly the affected ref. | ||
120 | |||
121 | =back | ||
122 | |||
123 | =head1 RULE MATCHING | ||
124 | |||
125 | The rule applies only if its I<PROJECT> and I<USER> parts match the project | ||
126 | which is being updated and the user who requests the update, its I<OP> | ||
127 | contains the opcode of the requested operation and I<REF> matches the affected | ||
128 | ref. Missing I<REF> and/or I<OP> are treated as a match. | ||
129 | |||
130 | If no rule applies, the operation is allowed. | ||
131 | |||
132 | For example, assume you have the following ACL file: | ||
133 | |||
134 | allow myprog %devel U heads/master | ||
135 | allow myprog %pm CDUR heads/ | ||
136 | allow myprog %pm C ^heads/tags/v\\d+$ | ||
137 | allow myprog admin CDUR | ||
138 | deny myprog all | ||
139 | |||
140 | Then the users from the B<devel> group will be able to push updates to | ||
141 | B<refs/heads/master>, the users from the B<pm> group will be allowed to do | ||
142 | anything with refs under B<refs/heads> and to create tags with names beginning | ||
143 | with B<v> and containing only digits afterwards, and the user B<admin> will | ||
144 | be allowed to do anything he pleases. No other users will be allowed to | ||
145 | update that repository. | ||
146 | |||
147 | =head1 CONFIGURATION SETTINGS | ||
148 | |||
149 | =over 4 | ||
150 | |||
151 | =item B<hooks.aclfile> STRING | ||
152 | |||
153 | Name of the ACL file. | ||
154 | |||
155 | =item B<hooks.acllog> STRING | ||
156 | |||
157 | Send log info to this file. | ||
158 | |||
159 | =item B<hooks.acldebug> BOOL | ||
160 | |||
161 | Enable debugging. | ||
162 | |||
163 | =item B<hooks.aclquiet> BOOL | ||
164 | |||
165 | Suppress diagnostics on stderr. | ||
166 | |||
167 | =item B<hooks.httpd-user> STRING | ||
168 | |||
169 | Name of the user httpd runs as. Define it if the repository can be | ||
170 | accessed via HTTP(S). If B<gitaclhook> is run as this user, it will | ||
171 | get the name of the user on behalf of which the update is performed | ||
172 | from the environment variable B<REMOTE_USER>. | ||
173 | |||
174 | =back | ||
175 | |||
176 | =head1 SEE ALSO | ||
177 | |||
178 | B<git-receive-pack>(1). | ||
179 | |||
180 | =head1 AUTHOR | ||
181 | |||
182 | Sergey Poznyakoff, <gray@gno.org> | ||
183 | |||
184 | =cut | ||
185 | |||
186 | my $debug = $ENV{GIT_UPDATE_DEBUG} > 0; | ||
187 | my $logfile; | ||
188 | my $quiet; | ||
189 | my ($user_name) = getpwuid $<; | ||
190 | my $git_dir = $ENV{GIT_DIR}; | ||
191 | my $ref = $ARGV[0]; | ||
192 | my $old = $ARGV[1]; | ||
193 | my $new = $ARGV[2]; | ||
194 | |||
195 | my $project_name; | ||
196 | my $op; | ||
197 | |||
198 | my %opstr = ('C' => 'create', | ||
199 | 'D' => 'delete', | ||
200 | 'U' => 'update', | ||
201 | 'R' => 'rewind/rebase'); | ||
202 | |||
203 | sub logmsg($$;$) { | ||
204 | return 0 unless $logfile; | ||
205 | |||
206 | my $status = shift; | ||
207 | my $message = shift; | ||
208 | my $loc = shift; | ||
209 | my $fd; | ||
210 | |||
211 | open($fd, $logfile); | ||
212 | if ($loc) { | ||
213 | print $fd "$status:$loc: $message\n"; | ||
214 | } else { | ||
215 | print $fd "$status: $message\n"; | ||
216 | } | ||
217 | close($fd); | ||
218 | } | ||
219 | |||
220 | sub deny($;$) { | ||
221 | my $msg = shift; | ||
222 | my $loc = shift; | ||
223 | |||
224 | logmsg("DENY", | ||
225 | "$project_name:$user_name:$opstr{$op}:$ref:$old:$new: $msg", | ||
226 | $loc); | ||
227 | |||
228 | print STDERR "debug: denied by $loc\n" if ($debug and $loc); | ||
229 | print STDERR "denied: $msg\n" unless $quiet; | ||
230 | exit 1; | ||
231 | } | ||
232 | |||
233 | sub allow($) { | ||
234 | logmsg("ALLOW", | ||
235 | "$project_name:$user_name:$opstr{$op}:$ref:$old:$new", | ||
236 | $_[0]); | ||
237 | print STDERR "debug: allow $_[0]\n" if $debug; | ||
238 | exit 0; | ||
239 | } | ||
240 | |||
241 | sub info($) { | ||
242 | logmsg("INFO", $_[0]); | ||
243 | print STDERR "info: $_[0]\n" if $debug; | ||
244 | } | ||
245 | |||
246 | sub git_value(@) { | ||
247 | my $fd; | ||
248 | |||
249 | open($fd,'-|','git',@_); | ||
250 | local $_ = <$fd>; | ||
251 | chop; | ||
252 | close($fd); | ||
253 | return $_; | ||
254 | } | ||
255 | |||
256 | sub match_user($) { | ||
257 | my $user = shift; | ||
258 | return 1 if ($user eq 'all'); | ||
259 | return 0 if ($user eq 'none'); | ||
260 | if ($user =~ /^%(.+)/) { | ||
261 |