Skip to content

Commit 719399b

Browse files
derrickstoleegitster
authored andcommitted
credential: add new interactive config option
When scripts or background maintenance wish to perform HTTP(S) requests, there is a risk that our stored credentials might be invalid. At the moment, this causes the credential helper to ping the user and block the process. Even if the credential helper does not ping the user, Git falls back to the 'askpass' method, which includes a direct ping to the user via the terminal. Even setting the 'core.askPass' config as something like 'echo' will causes Git to fallback to a terminal prompt. It uses git_terminal_prompt(), which finds the terminal from the environment and ignores whether stdin has been redirected. This can also block the process awaiting input. Create a new config option to prevent user interaction, favoring a failure to a blocked process. The chosen name, 'credential.interactive', is taken from the config option used by Git Credential Manager to already avoid user interactivity, so there is already one credential helper that integrates with this option. However, older versions of Git Credential Manager also accepted other string values, including 'auto', 'never', and 'always'. The modern use is to use a boolean value, but we should still be careful that some users could have these non-booleans. Further, we should respect 'never' the same as 'false'. This is respected by the implementation and test, but not mentioned in the documentation. The implementation for the Git interactions takes place within credential_getpass(). The method prototype is modified to return an 'int' instead of 'void'. This allows us to detect that no attempt was made to fill the given credential, changing the single caller slightly. Also, a new trace2 region is added around the interactive portion of the credential request. This provides a way to measure the amount of time spent in that region for commands that _are_ interactive. It also makes a conventient way to test that the config option works with 'test_region'. Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e29e5cf commit 719399b

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

Documentation/config/credential.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ credential.helper::
99
Note that multiple helpers may be defined. See linkgit:gitcredentials[7]
1010
for details and examples.
1111

12+
credential.interactive::
13+
By default, Git and any configured credential helpers will ask for
14+
user input when new credentials are required. Many of these helpers
15+
will succeed based on stored credentials if those credentials are
16+
still valid. To avoid the possibility of user interactivity from
17+
Git, set `credential.interactive=false`. Some credential helpers
18+
respect this option as well.
19+
1220
credential.useHttpPath::
1321
When acquiring credentials, consider the "path" component of an http
1422
or https URL to be important. Defaults to false. See

credential.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include "strbuf.h"
1212
#include "urlmatch.h"
1313
#include "git-compat-util.h"
14+
#include "trace2.h"
15+
#include "repository.h"
1416

1517
void credential_init(struct credential *c)
1618
{
@@ -249,14 +251,36 @@ static char *credential_ask_one(const char *what, struct credential *c,
249251
return xstrdup(r);
250252
}
251253

252-
static void credential_getpass(struct credential *c)
254+
static int credential_getpass(struct credential *c)
253255
{
256+
int interactive;
257+
char *value;
258+
if (!git_config_get_maybe_bool("credential.interactive", &interactive) &&
259+
!interactive) {
260+
trace2_data_intmax("credential", the_repository,
261+
"interactive/skipped", 1);
262+
return -1;
263+
}
264+
if (!git_config_get_string("credential.interactive", &value)) {
265+
int same = !strcmp(value, "never");
266+
free(value);
267+
if (same) {
268+
trace2_data_intmax("credential", the_repository,
269+
"interactive/skipped", 1);
270+
return -1;
271+
}
272+
}
273+
274+
trace2_region_enter("credential", "interactive", the_repository);
254275
if (!c->username)
255276
c->username = credential_ask_one("Username", c,
256277
PROMPT_ASKPASS|PROMPT_ECHO);
257278
if (!c->password)
258279
c->password = credential_ask_one("Password", c,
259280
PROMPT_ASKPASS);
281+
trace2_region_leave("credential", "interactive", the_repository);
282+
283+
return 0;
260284
}
261285

262286
int credential_has_capability(const struct credential_capability *capa,
@@ -499,8 +523,8 @@ void credential_fill(struct credential *c, int all_capabilities)
499523
c->helpers.items[i].string);
500524
}
501525

502-
credential_getpass(c);
503-
if (!c->username && !c->password && !c->credential)
526+
if (credential_getpass(c) ||
527+
(!c->username && !c->password && !c->credential))
504528
die("unable to get password from user");
505529
}
506530

t/t5551-http-fetch-smart.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,28 @@ test_expect_success 'clone from password-protected repository' '
186186
test_cmp expect actual
187187
'
188188

189+
test_expect_success 'credential.interactive=false skips askpass' '
190+
set_askpass bogus nonsense &&
191+
(
192+
GIT_TRACE2_EVENT="$(pwd)/interactive-true" &&
193+
export GIT_TRACE2_EVENT &&
194+
test_must_fail git clone --bare "$HTTPD_URL/auth/smart/repo.git" interactive-true-dir &&
195+
test_region credential interactive interactive-true &&
196+
197+
GIT_TRACE2_EVENT="$(pwd)/interactive-false" &&
198+
export GIT_TRACE2_EVENT &&
199+
test_must_fail git -c credential.interactive=false \
200+
clone --bare "$HTTPD_URL/auth/smart/repo.git" interactive-false-dir &&
201+
test_region ! credential interactive interactive-false &&
202+
203+
GIT_TRACE2_EVENT="$(pwd)/interactive-never" &&
204+
export GIT_TRACE2_EVENT &&
205+
test_must_fail git -c credential.interactive=never \
206+
clone --bare "$HTTPD_URL/auth/smart/repo.git" interactive-never-dir &&
207+
test_region ! credential interactive interactive-never
208+
)
209+
'
210+
189211
test_expect_success 'clone from auth-only-for-push repository' '
190212
echo two >expect &&
191213
set_askpass wrong &&

0 commit comments

Comments
 (0)