Skip to content

Commit 1182507

Browse files
peffgitster
authored andcommitted
credential: apply helper config
The functionality for credential storage helpers is already there; we just need to give the users a way to turn it on. This patch provides a "credential.helper" configuration variable which allows the user to provide one or more helper strings. Rather than simply matching credential.helper, we will also compare URLs in subsection headings to the current context. This means you can apply configuration to a subset of credentials. For example: [credential "https://example.com"] helper = foo would match a request for "https://example.com/foo.git", but not one for "https://kernel.org/foo.git". This is overkill for the "helper" variable, since users are unlikely to want different helpers for different sites (and since helpers run arbitrary code, they could do the matching themselves anyway). However, future patches will add new config variables where this extra feature will be more useful. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 148bb6a commit 1182507

File tree

4 files changed

+119
-1
lines changed

4 files changed

+119
-1
lines changed

credential.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,61 @@ void credential_clear(struct credential *c)
2222
credential_init(c);
2323
}
2424

25+
int credential_match(const struct credential *want,
26+
const struct credential *have)
27+
{
28+
#define CHECK(x) (!want->x || (have->x && !strcmp(want->x, have->x)))
29+
return CHECK(protocol) &&
30+
CHECK(host) &&
31+
CHECK(path) &&
32+
CHECK(username);
33+
#undef CHECK
34+
}
35+
36+
static int credential_config_callback(const char *var, const char *value,
37+
void *data)
38+
{
39+
struct credential *c = data;
40+
const char *key, *dot;
41+
42+
key = skip_prefix(var, "credential.");
43+
if (!key)
44+
return 0;
45+
46+
if (!value)
47+
return config_error_nonbool(var);
48+
49+
dot = strrchr(key, '.');
50+
if (dot) {
51+
struct credential want = CREDENTIAL_INIT;
52+
char *url = xmemdupz(key, dot - key);
53+
int matched;
54+
55+
credential_from_url(&want, url);
56+
matched = credential_match(&want, c);
57+
58+
credential_clear(&want);
59+
free(url);
60+
61+
if (!matched)
62+
return 0;
63+
key = dot + 1;
64+
}
65+
66+
if (!strcmp(key, "helper"))
67+
string_list_append(&c->helpers, value);
68+
69+
return 0;
70+
}
71+
72+
static void credential_apply_config(struct credential *c)
73+
{
74+
if (c->configured)
75+
return;
76+
git_config(credential_config_callback, c);
77+
c->configured = 1;
78+
}
79+
2580
static void credential_describe(struct credential *c, struct strbuf *out)
2681
{
2782
if (!c->protocol)
@@ -195,6 +250,8 @@ void credential_fill(struct credential *c)
195250
if (c->username && c->password)
196251
return;
197252

253+
credential_apply_config(c);
254+
198255
for (i = 0; i < c->helpers.nr; i++) {
199256
credential_do(c, c->helpers.items[i].string, "get");
200257
if (c->username && c->password)
@@ -215,6 +272,8 @@ void credential_approve(struct credential *c)
215272
if (!c->username || !c->password)
216273
return;
217274

275+
credential_apply_config(c);
276+
218277
for (i = 0; i < c->helpers.nr; i++)
219278
credential_do(c, c->helpers.items[i].string, "store");
220279
c->approved = 1;
@@ -224,6 +283,8 @@ void credential_reject(struct credential *c)
224283
{
225284
int i;
226285

286+
credential_apply_config(c);
287+
227288
for (i = 0; i < c->helpers.nr; i++)
228289
credential_do(c, c->helpers.items[i].string, "erase");
229290

credential.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
struct credential {
77
struct string_list helpers;
8-
unsigned approved:1;
8+
unsigned approved:1,
9+
configured:1;
910

1011
char *username;
1112
char *password;
@@ -25,5 +26,7 @@ void credential_reject(struct credential *);
2526

2627
int credential_read(struct credential *, FILE *);
2728
void credential_from_url(struct credential *, const char *url);
29+
int credential_match(const struct credential *have,
30+
const struct credential *want);
2831

2932
#endif /* CREDENTIAL_H */

t/t0300-credentials.sh

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,46 @@ test_expect_success 'internal getpass does not ask for known username' '
192192
EOF
193193
'
194194

195+
HELPER="!f() {
196+
cat >/dev/null
197+
echo username=foo
198+
echo password=bar
199+
}; f"
200+
test_expect_success 'respect configured credentials' '
201+
test_config credential.helper "$HELPER" &&
202+
check fill <<-\EOF
203+
--
204+
username=foo
205+
password=bar
206+
--
207+
EOF
208+
'
209+
210+
test_expect_success 'match configured credential' '
211+
test_config credential.https://example.com.helper "$HELPER" &&
212+
check fill <<-\EOF
213+
protocol=https
214+
host=example.com
215+
path=repo.git
216+
--
217+
username=foo
218+
password=bar
219+
--
220+
EOF
221+
'
222+
223+
test_expect_success 'do not match configured credential' '
224+
test_config credential.https://foo.helper "$HELPER" &&
225+
check fill <<-\EOF
226+
protocol=https
227+
host=bar
228+
--
229+
username=askpass-username
230+
password=askpass-password
231+
--
232+
askpass: Username for '\''https://bar'\'':
233+
askpass: Password for '\''https://askpass-username@bar'\'':
234+
EOF
235+
'
236+
195237
test_done

t/t5550-http-fetch.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ test_expect_success 'http auth can request both user and pass' '
101101
expect_askpass both user@host
102102
'
103103

104+
test_expect_success 'http auth respects credential helper config' '
105+
test_config_global credential.helper "!f() {
106+
cat >/dev/null
107+
echo username=user@host
108+
echo password=user@host
109+
}; f" &&
110+
>askpass-query &&
111+
echo wrong >askpass-response &&
112+
git clone "$HTTPD_URL/auth/repo.git" clone-auth-helper &&
113+
expect_askpass none
114+
'
115+
104116
test_expect_success 'fetch changes via http' '
105117
echo content >>file &&
106118
git commit -a -m two &&

0 commit comments

Comments
 (0)