Skip to content

Commit

Permalink
credential: apply helper config
Browse files Browse the repository at this point in the history
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 <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Jeff King authored and Junio C Hamano committed Dec 12, 2011
1 parent 148bb6a commit 1182507
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 1 deletion.
61 changes: 61 additions & 0 deletions credential.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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");

Expand Down
5 changes: 4 additions & 1 deletion credential.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

struct credential {
struct string_list helpers;
unsigned approved:1;
unsigned approved:1,
configured:1;

char *username;
char *password;
Expand All @@ -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 */
42 changes: 42 additions & 0 deletions t/t0300-credentials.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 12 additions & 0 deletions t/t5550-http-fetch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down

0 comments on commit 1182507

Please sign in to comment.