credential: let helpers tell us to quit

When we are trying to fill a credential, we loop over the set of defined credential-helpers, then fall back to running askpass, and then finally prompt on the terminal. Helpers which cannot find a credential are free to tell us nothing, but they cannot currently ask us to stop prompting. This patch lets them provide a "quit" attribute, which asks us to stop the process entirely (avoiding running more helpers, as well as the askpass/terminal prompt). This has a few possible uses: 1. A helper which prompts the user itself (e.g., in a dialog) can provide a "cancel" button to the user to stop further prompts. 2. Some helpers may know that prompting cannot possibly work. For example, if their role is to broker a ticket from an external auth system and that auth system cannot be contacted, there is no point in continuing (we need a ticket to authenticate, and the user cannot provide one by typing it in). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Dec 3, 2014 at 22:46 UTC 59b386526a6cdd0289cdf35dd8038ae1bdfd630f
4 files changed +19 -1
Documentation/technical/api-credentials.txt
+4 -1
@@ -248,7 +248,10 @@ FORMAT` in linkgit:git-credential[7] for a detailed specification).
248 For a `get` operation, the helper should produce a list of attributes
249 on stdout in the same format. A helper is free to produce a subset, or
250 even no values at all if it has nothing useful to provide. Any provided
251 -attributes will overwrite those already known about by Git.
251 +attributes will overwrite those already known about by Git. If a helper
252 +outputs a `quit` attribute with a value of `true` or `1`, no further
253 +helpers will be consulted, nor will the user be prompted (if no
254 +credential has been provided, the operation will then fail).
255
256 For a `store` or `erase` operation, the helper's output is ignored.
257 If it fails to perform the requested operation, it may complain to
credential.c
+5
@@ -173,6 +173,8 @@ int credential_read(struct credential *c, FILE *fp)
173 c->path = xstrdup(value);
174 } else if (!strcmp(key, "url")) {
175 credential_from_url(c, value);
176 + } else if (!strcmp(key, "quit")) {
177 + c->quit = !!git_config_bool("quit", value);
178 }
179 /*
180 * Ignore other lines; we don't know what they mean, but
@@ -275,6 +277,9 @@ void credential_fill(struct credential *c)
277 credential_do(c, c->helpers.items[i].string, "get");
278 if (c->username && c->password)
279 return;
280 + if (c->quit)
281 + die("credential helper '%s' told us to quit",
282 + c->helpers.items[i].string);
283 }
284
285 credential_getpass(c);
credential.h
+1
@@ -7,6 +7,7 @@ struct credential {
7 struct string_list helpers;
8 unsigned approved:1,
9 configured:1,
10 + quit:1,
11 use_http_path:1;
12
13 char *username;
t/t0300-credentials.sh
+9
@@ -289,4 +289,13 @@ test_expect_success 'http paths can be part of context' '
289 EOF
290 '
291
292 +test_expect_success 'helpers can abort the process' '
293 + test_must_fail git \
294 + -c credential.helper="!f() { echo quit=1; }; f" \
295 + -c credential.helper="verbatim foo bar" \
296 + credential fill >stdout &&
297 + >expect &&
298 + test_cmp expect stdout
299 +'
300 +
301 test_done