credential-cache: implement authtype capability

Now that we have full support in Git for the authtype capability, let's add support to the cache credential helper. When parsing data, we always set the initial capabilities because we're the helper, and we need both the initial and helper capabilities to be set in order to have the helper capabilities take effect. When emitting data, always emit the supported capability and make sure we emit items only if we have them and they're supported by the caller. Since we may no longer have a username or password, be sure to emit those conditionally as well so we don't segfault on a NULL pointer. Similarly, when comparing credentials, consider both the password and credential fields when we're matching passwords. Adjust the partial credential detection code so that we can store credentials missing a username or password as long as they have an authtype and credential. Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

brian m. carlson committed Apr 17, 2024 at 00:02 UTC 40220f48b1895c7c4c824c3c33576399128fbc0f
4 files changed +28 -6
builtin/credential-cache--daemon.c
+17 -3
@@ -115,6 +115,8 @@ static int read_request(FILE *fh, struct credential *c,
115 return error("client sent bogus timeout line: %s", item.buf);
116 *timeout = atoi(p);
117
118 + credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL);
119 +
120 if (credential_read(c, fh, CREDENTIAL_OP_HELPER) < 0)
121 return -1;
122 return 0;
@@ -131,8 +133,18 @@ static void serve_one_client(FILE *in, FILE *out)
133 else if (!strcmp(action.buf, "get")) {
134 struct credential_cache_entry *e = lookup_credential(&c);
135 if (e) {
134 - fprintf(out, "username=%s\n", e->item.username);
135 - fprintf(out, "password=%s\n", e->item.password);
136 + e->item.capa_authtype.request_initial = 1;
137 + e->item.capa_authtype.request_helper = 1;
138 +
139 + fprintf(out, "capability[]=authtype\n");
140 + if (e->item.username)
141 + fprintf(out, "username=%s\n", e->item.username);
142 + if (e->item.password)
143 + fprintf(out, "password=%s\n", e->item.password);
144 + if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.authtype)
145 + fprintf(out, "authtype=%s\n", e->item.authtype);
146 + if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.credential)
147 + fprintf(out, "credential=%s\n", e->item.credential);
148 if (e->item.password_expiry_utc != TIME_MAX)
149 fprintf(out, "password_expiry_utc=%"PRItime"\n",
150 e->item.password_expiry_utc);
@@ -157,8 +169,10 @@ static void serve_one_client(FILE *in, FILE *out)
169 else if (!strcmp(action.buf, "store")) {
170 if (timeout < 0)
171 warning("cache client didn't specify a timeout");
160 - else if (!c.username || !c.password)
172 + else if ((!c.username || !c.password) && (!c.authtype && !c.credential))
173 warning("cache client gave us a partial credential");
174 + else if (c.ephemeral)
175 + warning("not storing ephemeral credential");
176 else {
177 remove_credential(&c, 0);
178 cache_credential(&c, timeout);
credential.c
+4 -3
@@ -80,7 +80,8 @@ int credential_match(const struct credential *want,
80 CHECK(host) &&
81 CHECK(path) &&
82 CHECK(username) &&
83 - (!match_password || CHECK(password));
83 + (!match_password || CHECK(password)) &&
84 + (!match_password || CHECK(credential));
85 #undef CHECK
86 }
87
@@ -248,8 +249,8 @@ static void credential_getpass(struct credential *c)
249 PROMPT_ASKPASS);
250 }
251
251 -static int credential_has_capability(const struct credential_capability *capa,
252 - enum credential_op_type op_type)
252 +int credential_has_capability(const struct credential_capability *capa,
253 + enum credential_op_type op_type)
254 {
255 /*
256 * We're checking here if each previous step indicated that we had the
credential.h
+6
@@ -263,6 +263,12 @@ void credential_clear_secrets(struct credential *c);
263 */
264 void credential_next_state(struct credential *c);
265
266 +/**
267 + * Return true if the capability is enabled for an operation of op_type.
268 + */
269 +int credential_has_capability(const struct credential_capability *capa,
270 + enum credential_op_type op_type);
271 +
272 int credential_read(struct credential *, FILE *,
273 enum credential_op_type);
274 void credential_write(const struct credential *, FILE *,
t/t0301-credential-cache.sh
+1
@@ -31,6 +31,7 @@ test_atexit 'git credential-cache exit'
31 helper_test cache
32 helper_test_password_expiry_utc cache
33 helper_test_oauth_refresh_token cache
34 +helper_test_authtype cache
35
36 test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
37 test_when_finished "