git-remote-mediawiki: add credential support

The previous version implemented the possibility to log in a wiki, but the username and password had to be provided as configuration variables. We add the possibility to use the Git credential system to prompt the password. The support if implemented with generic functions that mimic the C API, designed to be usable from other contexts in the future (i.e. they may migrate to Git.pm if someone is interested). While we're there, do a bit of refactoring in mw_connect_maybe. Based on patch by: Javier Roucher Iglesias <Javier.Roucher-Iglesias@ensimag.imag.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Matthieu Moy committed Jun 24, 2012 at 13:40 UTC a7271ad1dccaf1a1875992520204debb37ed78bb
1 file changed +94 -12
contrib/mw-to-git/git-remote-mediawiki
+94 -12
@@ -43,6 +43,8 @@ use encoding 'utf8';
43 binmode STDERR, ":utf8";
44
45 use URI::Escape;
46 +use IPC::Open2;
47 +
48 use warnings;
49
50 # Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced
@@ -151,28 +153,108 @@ while (<STDIN>) {
153
154 ########################## Functions ##############################
155
156 +## credential API management (generic functions)
157 +
158 +sub credential_from_url {
159 + my $url = shift;
160 + my $parsed = URI->new($url);
161 + my %credential;
162 +
163 + if ($parsed->scheme) {
164 + $credential{protocol} = $parsed->scheme;
165 + }
166 + if ($parsed->host) {
167 + $credential{host} = $parsed->host;
168 + }
169 + if ($parsed->path) {
170 + $credential{path} = $parsed->path;
171 + }
172 + if ($parsed->userinfo) {
173 + if ($parsed->userinfo =~ /([^:]*):(.*)/) {
174 + $credential{username} = $1;
175 + $credential{password} = $2;
176 + } else {
177 + $credential{username} = $parsed->userinfo;
178 + }
179 + }
180 +
181 + return %credential;
182 +}
183 +
184 +sub credential_read {
185 + my %credential;
186 + my $reader = shift;
187 + my $op = shift;
188 + while (<$reader>) {
189 + my ($key, $value) = /([^=]*)=(.*)/;
190 + if (not defined $key) {
191 + die "ERROR receiving response from git credential $op:\n$_\n";
192 + }
193 + $credential{$key} = $value;
194 + }
195 + return %credential;
196 +}
197 +
198 +sub credential_write {
199 + my $credential = shift;
200 + my $writer = shift;
201 + while (my ($key, $value) = each(%$credential) ) {
202 + if ($value) {
203 + print $writer "$key=$value\n";
204 + }
205 + }
206 +}
207 +
208 +sub credential_run {
209 + my $op = shift;
210 + my $credential = shift;
211 + my $pid = open2(my $reader, my $writer, "git credential $op");
212 + credential_write($credential, $writer);
213 + print $writer "\n";
214 + close($writer);
215 +
216 + if ($op eq "fill") {
217 + %$credential = credential_read($reader, $op);
218 + } else {
219 + if (<$reader>) {
220 + die "ERROR while running git credential $op:\n$_";
221 + }
222 + }
223 + close($reader);
224 + waitpid($pid, 0);
225 + my $child_exit_status = $? >> 8;
226 + if ($child_exit_status != 0) {
227 + die "'git credential $op' failed with code $child_exit_status.";
228 + }
229 +}
230 +
231 # MediaWiki API instance, created lazily.
232 my $mediawiki;
233
234 sub mw_connect_maybe {
235 if ($mediawiki) {
159 - return;
236 + return;
237 }
238 $mediawiki = MediaWiki::API->new;
239 $mediawiki->{config}->{api_url} = "$url/api.php";
240 if ($wiki_login) {
164 - if (!$mediawiki->login({
165 - lgname => $wiki_login,
166 - lgpassword => $wiki_passwd,
167 - lgdomain => $wiki_domain,
168 - })) {
169 - print STDERR "Failed to log in mediawiki user \"$wiki_login\" on $url\n";
170 - print STDERR "(error " .
171 - $mediawiki->{error}->{code} . ': ' .
172 - $mediawiki->{error}->{details} . ")\n";
173 - exit 1;
241 + my %credential = credential_from_url($url);
242 + $credential{username} = $wiki_login;
243 + $credential{password} = $wiki_passwd;
244 + credential_run("fill", \%credential);
245 + my $request = {lgname => $credential{username},
246 + lgpassword => $credential{password},
247 + lgdomain => $wiki_domain};
248 + if ($mediawiki->login($request)) {
249 + credential_run("approve", \%credential);
250 + print STDERR "Logged in mediawiki user \"$credential{username}\".\n";
251 } else {
175 - print STDERR "Logged in with user \"$wiki_login\".\n";
252 + print STDERR "Failed to log in mediawiki user \"$credential{username}\" on $url\n";
253 + print STDERR " (error " .
254 + $mediawiki->{error}->{code} . ': ' .
255 + $mediawiki->{error}->{details} . ")\n";
256 + credential_run("reject", \%credential);
257 + exit 1;
258 }
259 }
260 }