summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--credential.c61
-rw-r--r--credential.h5
-rwxr-xr-xt/t0300-credentials.sh42
-rwxr-xr-xt/t5550-http-fetch.sh12
4 files changed, 119 insertions, 1 deletions
diff --git a/credential.c b/credential.c
index c349b9aac3..96be1c22dd 100644
--- a/credential.c
+++ b/credential.c
@@ -22,6 +22,61 @@ void credential_clear(struct credential *c)
credential_init(c);
}
+int credential_match(const struct credential *want,
+ const struct credential *have)
+{
+#define CHECK(x) (!want->x || (have->x && !strcmp(want->x, have->x)))
+ return CHECK(protocol) &&
+ CHECK(host) &&
+ CHECK(path) &&
+ CHECK(username);
+#undef CHECK
+}
+
+static int credential_config_callback(const char *var, const char *value,
+ void *data)
+{
+ struct credential *c = data;
+ const char *key, *dot;
+
+ key = skip_prefix(var, "credential.");
+ if (!key)
+ return 0;
+
+ if (!value)
+ return config_error_nonbool(var);
+
+ dot = strrchr(key, '.');
+ if (dot) {
+ struct credential want = CREDENTIAL_INIT;
+ char *url = xmemdupz(key, dot - key);
+ int matched;
+
+ credential_from_url(&want, url);
+ matched = credential_match(&want, c);
+
+ credential_clear(&want);
+ free(url);
+
+ if (!matched)
+ return 0;
+ key = dot + 1;
+ }
+
+ if (!strcmp(key, "helper"))
+ string_list_append(&c->helpers, value);
+
+ return 0;
+}
+
+static void credential_apply_config(struct credential *c)
+{
+ if (c->configured)
+ return;
+ git_config(credential_config_callback, c);
+ c->configured = 1;
+}
+
static void credential_describe(struct credential *c, struct strbuf *out)
{
if (!c->protocol)
@@ -195,6 +250,8 @@ void credential_fill(struct credential *c)
if (c->username && c->password)
return;
+ credential_apply_config(c);
+
for (i = 0; i < c->helpers.nr; i++) {
credential_do(c, c->helpers.items[i].string, "get");
if (c->username && c->password)
@@ -215,6 +272,8 @@ void credential_approve(struct credential *c)
if (!c->username || !c->password)
return;
+ credential_apply_config(c);
+
for (i = 0; i < c->helpers.nr; i++)
credential_do(c, c->helpers.items[i].string, "store");
c->approved = 1;
@@ -224,6 +283,8 @@ void credential_reject(struct credential *c)
{
int i;
+ credential_apply_config(c);
+
for (i = 0; i < c->helpers.nr; i++)
credential_do(c, c->helpers.items[i].string, "erase");
diff --git a/credential.h b/credential.h
index 8a6d162e7b..e5042723a8 100644
--- a/credential.h
+++ b/credential.h
@@ -5,7 +5,8 @@
struct credential {
struct string_list helpers;
- unsigned approved:1;
+ unsigned approved:1,
+ configured:1;
char *username;
char *password;
@@ -25,5 +26,7 @@ void credential_reject(struct credential *);
int credential_read(struct credential *, FILE *);
void credential_from_url(struct credential *, const char *url);
+int credential_match(const struct credential *have,
+ const struct credential *want);
#endif /* CREDENTIAL_H */
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index 81a455f4c3..42d0f5b707 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -192,4 +192,46 @@ test_expect_success 'internal getpass does not ask for known username' '
EOF
'
+HELPER="!f() {
+ cat >/dev/null
+ echo username=foo
+ echo password=bar
+ }; f"
+test_expect_success 'respect configured credentials' '
+ test_config credential.helper "$HELPER" &&
+ check fill <<-\EOF
+ --
+ username=foo
+ password=bar
+ --
+ EOF
+'
+
+test_expect_success 'match configured credential' '
+ test_config credential.https://example.com.helper "$HELPER" &&
+ check fill <<-\EOF
+ protocol=https
+ host=example.com
+ path=repo.git
+ --
+ username=foo
+ password=bar
+ --
+ EOF
+'
+
+test_expect_success 'do not match configured credential' '
+ test_config credential.https://foo.helper "$HELPER" &&
+ check fill <<-\EOF
+ protocol=https
+ host=bar
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://bar'\'':
+ askpass: Password for '\''https://askpass-username@bar'\'':
+ EOF
+'
+
test_done
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
index 398a2d29a4..c59908fe77 100755
--- a/t/t5550-http-fetch.sh
+++ b/t/t5550-http-fetch.sh
@@ -101,6 +101,18 @@ test_expect_success 'http auth can request both user and pass' '
expect_askpass both user@host
'
+test_expect_success 'http auth respects credential helper config' '
+ test_config_global credential.helper "!f() {
+ cat >/dev/null
+ echo username=user@host
+ echo password=user@host
+ }; f" &&
+ >askpass-query &&
+ echo wrong >askpass-response &&
+ git clone "$HTTPD_URL/auth/repo.git" clone-auth-helper &&
+ expect_askpass none
+'
+
test_expect_success 'fetch changes via http' '
echo content >>file &&
git commit -a -m two &&