Skip to content

Commit 8470c94

Browse files
bk2204gitster
authored andcommitted
credential: add an argument to keep state
Until now, our credential code has mostly deal with usernames and passwords and we've let libcurl deal with the variant of authentication to be used. However, now that we have the credential value, the credential helper can take control of the authentication, so the value provided might be something that's generated, such as a Digest hash value. In such a case, it would be helpful for a credential helper that gets an erase or store command to be able to keep track of an identifier for the original secret that went into the computation. Furthermore, some types of authentication, such as NTLM and Kerberos, actually need two round trips to authenticate, which will require that the credential helper keep some state. In order to allow for these use cases and others, allow storing state in a field called "state[]". This value is passed back to the credential helper that created it, which avoids confusion caused by parsing values from different helpers. Signed-off-by: brian m. carlson <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ad9bb6d commit 8470c94

File tree

4 files changed

+71
-12
lines changed

4 files changed

+71
-12
lines changed

Documentation/git-credential.txt

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@ can determine whether the operation was successful.
211211
This value should not be sent unless the appropriate capability (see below) is
212212
provided on input.
213213

214+
`state[]`::
215+
This value provides an opaque state that will be passed back to this helper
216+
if it is called again. Each different credential helper may specify this
217+
once. The value should include a prefix unique to the credential helper and
218+
should ignore values that don't match its prefix.
219+
+
220+
This value should not be sent unless the appropriate capability (see below) is
221+
provided on input.
222+
214223
`wwwauth[]`::
215224

216225
When an HTTP response is received by Git that includes one or more
@@ -223,18 +232,19 @@ they appear in the HTTP response. This attribute is 'one-way' from Git
223232
to pass additional information to credential helpers.
224233

225234
`capability[]`::
226-
This signals that the caller supports the capability in question.
227-
This can be used to provide better, more specific data as part of the
235+
This signals that Git, or the helper, as appropriate, supports the capability
236+
in question. This can be used to provide better, more specific data as part
237+
of the protocol. A `capability[]` directive must precede any value depending
238+
on it and these directives _should_ be the first item announced in the
228239
protocol.
229240
+
230-
The only capability currently supported is `authtype`, which indicates that the
231-
`authtype`, `credential`, and `ephemeral` values are understood. It is not
232-
obligatory to use these values in such a case, but they should not be provided
233-
without this capability.
241+
There are two currently supported capabilities. The first is `authtype`, which
242+
indicates that the `authtype`, `credential`, and `ephemeral` values are
243+
understood. The second is `state`, which indicates that the `state[]` and
244+
`continue` values are understood.
234245
+
235-
Callers of `git credential` and credential helpers should emit the
236-
capabilities they support unconditionally, and Git will gracefully
237-
handle passing them on.
246+
It is not obligatory to use the additional features just because the capability
247+
is supported, but they should not be provided without the capability.
238248

239249
Unrecognised attributes and capabilities are silently discarded.
240250

credential.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ void credential_clear(struct credential *c)
3030
free(c->authtype);
3131
string_list_clear(&c->helpers, 0);
3232
strvec_clear(&c->wwwauth_headers);
33+
strvec_clear(&c->state_headers);
3334

3435
credential_init(c);
3536
}
@@ -293,8 +294,13 @@ int credential_read(struct credential *c, FILE *fp,
293294
c->ephemeral = !!git_config_bool("ephemeral", value);
294295
} else if (!strcmp(key, "wwwauth[]")) {
295296
strvec_push(&c->wwwauth_headers, value);
296-
} else if (!strcmp(key, "capability[]") && !strcmp(value, "authtype")) {
297-
credential_set_capability(&c->capa_authtype, op_type);
297+
} else if (!strcmp(key, "state[]")) {
298+
strvec_push(&c->state_headers, value);
299+
} else if (!strcmp(key, "capability[]")) {
300+
if (!strcmp(value, "authtype"))
301+
credential_set_capability(&c->capa_authtype, op_type);
302+
else if (!strcmp(value, "state"))
303+
credential_set_capability(&c->capa_state, op_type);
298304
} else if (!strcmp(key, "password_expiry_utc")) {
299305
errno = 0;
300306
c->password_expiry_utc = parse_timestamp(value, NULL, 10);
@@ -337,8 +343,12 @@ static void credential_write_item(FILE *fp, const char *key, const char *value,
337343
void credential_write(const struct credential *c, FILE *fp,
338344
enum credential_op_type op_type)
339345
{
340-
if (credential_has_capability(&c->capa_authtype, op_type)) {
346+
if (credential_has_capability(&c->capa_authtype, op_type))
341347
credential_write_item(fp, "capability[]", "authtype", 0);
348+
if (credential_has_capability(&c->capa_state, op_type))
349+
credential_write_item(fp, "capability[]", "state", 0);
350+
351+
if (credential_has_capability(&c->capa_authtype, op_type)) {
342352
credential_write_item(fp, "authtype", c->authtype, 0);
343353
credential_write_item(fp, "credential", c->credential, 0);
344354
if (c->ephemeral)
@@ -357,6 +367,10 @@ void credential_write(const struct credential *c, FILE *fp,
357367
}
358368
for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
359369
credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
370+
if (credential_has_capability(&c->capa_state, op_type)) {
371+
for (size_t i = 0; i < c->state_headers.nr; i++)
372+
credential_write_item(fp, "state[]", c->state_headers.v[i], 0);
373+
}
360374
}
361375

362376
static int run_credential_helper(struct credential *c,

credential.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ struct credential {
144144
*/
145145
struct strvec wwwauth_headers;
146146

147+
/**
148+
* A `strvec` of state headers from credential helpers.
149+
*/
150+
struct strvec state_headers;
151+
147152
/**
148153
* Internal use only. Keeps track of if we previously matched against a
149154
* WWW-Authenticate header line in order to re-fold future continuation
@@ -159,6 +164,7 @@ struct credential {
159164
username_from_proto:1;
160165

161166
struct credential_capability capa_authtype;
167+
struct credential_capability capa_state;
162168

163169
char *username;
164170
char *password;
@@ -180,6 +186,7 @@ struct credential {
180186
.helpers = STRING_LIST_INIT_DUP, \
181187
.password_expiry_utc = TIME_MAX, \
182188
.wwwauth_headers = STRVEC_INIT, \
189+
.state_headers = STRVEC_INIT, \
183190
}
184191

185192
/* Initialize a credential structure, setting all fields to empty. */

t/t0300-credentials.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,12 @@ test_expect_success 'setup helper scripts' '
4646
credential=$1; shift
4747
. ./dump
4848
echo capability[]=authtype
49+
echo capability[]=state
4950
test -z "${capability##*authtype*}" || exit 0
5051
test -z "$authtype" || echo authtype=$authtype
5152
test -z "$credential" || echo credential=$credential
53+
test -z "${capability##*state*}" || exit 0
54+
echo state[]=verbatim-cred:foo
5255
EOF
5356
5457
write_script git-credential-verbatim-ephemeral <<-\EOF &&
@@ -129,6 +132,28 @@ test_expect_success 'credential_fill invokes helper with ephemeral credential' '
129132
verbatim-ephemeral: host=example.com
130133
EOF
131134
'
135+
test_expect_success 'credential_fill invokes helper with credential and state' '
136+
check fill "verbatim-cred Bearer token" <<-\EOF
137+
capability[]=authtype
138+
capability[]=state
139+
protocol=http
140+
host=example.com
141+
--
142+
capability[]=authtype
143+
capability[]=state
144+
authtype=Bearer
145+
credential=token
146+
protocol=http
147+
host=example.com
148+
state[]=verbatim-cred:foo
149+
--
150+
verbatim-cred: get
151+
verbatim-cred: capability[]=authtype
152+
verbatim-cred: capability[]=state
153+
verbatim-cred: protocol=http
154+
verbatim-cred: host=example.com
155+
EOF
156+
'
132157

133158
test_expect_success 'credential_fill invokes multiple helpers' '
134159
check fill useless "verbatim foo bar" <<-\EOF
@@ -152,6 +177,7 @@ test_expect_success 'credential_fill invokes multiple helpers' '
152177
test_expect_success 'credential_fill response does not get capabilities when helpers are incapable' '
153178
check fill useless "verbatim foo bar" <<-\EOF
154179
capability[]=authtype
180+
capability[]=state
155181
protocol=http
156182
host=example.com
157183
--
@@ -162,10 +188,12 @@ test_expect_success 'credential_fill response does not get capabilities when hel
162188
--
163189
useless: get
164190
useless: capability[]=authtype
191+
useless: capability[]=state
165192
useless: protocol=http
166193
useless: host=example.com
167194
verbatim: get
168195
verbatim: capability[]=authtype
196+
verbatim: capability[]=state
169197
verbatim: protocol=http
170198
verbatim: host=example.com
171199
EOF

0 commit comments

Comments
 (0)