git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Patrick Steinhardt <ps@pks.im>
To: "brian m. carlson" <sandals@crustytoothpaste.net>
Cc: git@vger.kernel.org, Junio C Hamano <gitster@pobox.com>,
	Matthew John Cheetham <mjcheetham@outlook.com>,
	M Hickford <mirth.hickford@gmail.com>
Subject: Re: [PATCH 05/13] credential: gate new fields on capability
Date: Wed, 27 Mar 2024 09:02:39 +0100	[thread overview]
Message-ID: <ZgPSn_D8luXQyZGf@tanuki> (raw)
In-Reply-To: <20240324011301.1553072-6-sandals@crustytoothpaste.net>

[-- Attachment #1: Type: text/plain, Size: 19755 bytes --]

On Sun, Mar 24, 2024 at 01:12:53AM +0000, brian m. carlson wrote:
> We support the new credential and authtype fields, but we lack a way to
> indicate to a credential helper that we'd like them to be used.  Without
> some sort of indication, the credential helper doesn't know if it should
> try to provide us a username and password, or a pre-encoded credential.
> For example, the helper might prefer a more restricted Bearer token if
> pre-encoded credentials are possible, but might have to fall back to
> more general username and password if not.
> 
> Let's provide a simple way to indicate whether Git (or, for that matter,
> the helper) is capable of understanding the authtype and credential
> fields.  We send this capability when we generate a request, and the
> other side may reply to indicate to us that it does, too.
> 
> For now, don't enable sending capabilities for the HTTP code.  In a
> future commit, we'll introduce appropriate handling for that code,
> which requires more in-depth work.
> 
> The logic for determining whether a capability is supported may seem
> complex, but it is not.  At each stage, we emit the capability to the
> following stage if all preceding stages have declared it.  Thus, if the
> caller to git credential fill didn't declare it, then we won't send it
> to the helper, and if fill's caller did send but the helper doesn't
> understand it, then we won't send it on in the response.  If we're an
> internal user, then we know about all capabilities and will request
> them.
> 
> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
> ---
>  builtin/credential-cache--daemon.c |   2 +-
>  builtin/credential-store.c         |   2 +-
>  builtin/credential.c               |   6 +-
>  credential.c                       |  58 ++++++++++++++--
>  credential.h                       |  30 +++++++-
>  http.c                             |  10 +--
>  imap-send.c                        |   2 +-
>  remote-curl.c                      |   4 +-
>  t/t0300-credentials.sh             | 107 ++++++++++++++++++++++++++++-
>  9 files changed, 197 insertions(+), 24 deletions(-)
> 
> diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
> index 3a6a750a8e..ccbcf99ac1 100644
> --- a/builtin/credential-cache--daemon.c
> +++ b/builtin/credential-cache--daemon.c
> @@ -115,7 +115,7 @@ static int read_request(FILE *fh, struct credential *c,
>  		return error("client sent bogus timeout line: %s", item.buf);
>  	*timeout = atoi(p);
>  
> -	if (credential_read(c, fh) < 0)
> +	if (credential_read(c, fh, CREDENTIAL_OP_HELPER) < 0)
>  		return -1;
>  	return 0;
>  }
> diff --git a/builtin/credential-store.c b/builtin/credential-store.c
> index 4a492411bb..494c809332 100644
> --- a/builtin/credential-store.c
> +++ b/builtin/credential-store.c
> @@ -205,7 +205,7 @@ int cmd_credential_store(int argc, const char **argv, const char *prefix)
>  	if (!fns.nr)
>  		die("unable to set up default path; use --file");
>  
> -	if (credential_read(&c, stdin) < 0)
> +	if (credential_read(&c, stdin, CREDENTIAL_OP_HELPER) < 0)
>  		die("unable to read credential");
>  
>  	if (!strcmp(op, "get"))
> diff --git a/builtin/credential.c b/builtin/credential.c
> index 7010752987..5123dabcf1 100644
> --- a/builtin/credential.c
> +++ b/builtin/credential.c
> @@ -17,12 +17,12 @@ int cmd_credential(int argc, const char **argv, const char *prefix UNUSED)
>  		usage(usage_msg);
>  	op = argv[1];
>  
> -	if (credential_read(&c, stdin) < 0)
> +	if (credential_read(&c, stdin, CREDENTIAL_OP_INITIAL) < 0)
>  		die("unable to read credential from stdin");
>  
>  	if (!strcmp(op, "fill")) {
> -		credential_fill(&c);
> -		credential_write(&c, stdout);
> +		credential_fill(&c, 0);
> +		credential_write(&c, stdout, CREDENTIAL_OP_RESPONSE);
>  	} else if (!strcmp(op, "approve")) {
>  		credential_approve(&c);
>  	} else if (!strcmp(op, "reject")) {
> diff --git a/credential.c b/credential.c
> index c521822e5a..f2a26b8672 100644
> --- a/credential.c
> +++ b/credential.c
> @@ -34,6 +34,11 @@ void credential_clear(struct credential *c)
>  	credential_init(c);
>  }
>  
> +static void credential_set_all_capabilities(struct credential *c)
> +{
> +	c->capa_authtype.request_initial = 1;
> +}
> +
>  int credential_match(const struct credential *want,
>  		     const struct credential *have, int match_password)
>  {
> @@ -210,7 +215,39 @@ static void credential_getpass(struct credential *c)
>  						 PROMPT_ASKPASS);
>  }
>  
> -int credential_read(struct credential *c, FILE *fp)
> +static void credential_set_capability(struct credential_capability *capa, int op_type)
> +{
> +	switch (op_type) {
> +	case CREDENTIAL_OP_INITIAL:
> +		capa->request_initial = 1;
> +		break;
> +	case CREDENTIAL_OP_HELPER:
> +		capa->request_helper = 1;
> +		break;
> +	case CREDENTIAL_OP_RESPONSE:
> +		capa->response = 1;
> +		break;
> +	}
> +}
> +
> +static int credential_has_capability(const struct credential_capability *capa, int op_type)
> +{
> +	/*
> +	 * We're checking here if each previous step indicated that we had the
> +	 * capability.  If it did, then we want to pass it along; conversely, if
> +	 * it did not, we don't want to report that to our caller.
> +	 */
> +	switch (op_type) {
> +	case CREDENTIAL_OP_HELPER:
> +		return capa->request_initial;
> +	case CREDENTIAL_OP_RESPONSE:
> +		return capa->request_initial && capa->request_helper;
> +	default:
> +		return 0;
> +	}
> +}

I think I'm missing the bigger picture here, so please bear with me.

What you provide here is simply an `op_type` that indicates the phase we
are currently in and thus allows us to check whether all of the
preceding phases had the capability set. But to me it seems like a phase
and the actual capability should be different things. So why is it that
the capability seems to be a mere boolean value instead of something
like a bitfield indicating whether a specific capability is supported or
not? Or is all of this infra really only to support a single capability,
namely the credential capability?

I'm mostly coming from the angle of how capabilities work with remote
helpers. When asked, the helper will announce a set of capabilities that
it supports, e.g. "capabilities stateless-connect". So from thereon the
client of the helper knows that it can assume "stateless-connect" to be
understood by the helper.

I would have expected capabilities to work similarly for the credential
helper, where it announces "I know to handle pre-encoded credentials".
But given that I have basically no clue at all for how the credential
helper works there may very well be good reasons why things work so
differently here.

> +int credential_read(struct credential *c, FILE *fp, int op_type)
>  {
>  	struct strbuf line = STRBUF_INIT;
>  
> @@ -249,6 +286,8 @@ int credential_read(struct credential *c, FILE *fp)
>  			c->path = xstrdup(value);
>  		} else if (!strcmp(key, "wwwauth[]")) {
>  			strvec_push(&c->wwwauth_headers, value);
> +		} else if (!strcmp(key, "capability[]") && !strcmp(value, "authtype")) {
> +			credential_set_capability(&c->capa_authtype, op_type);
>  		} else if (!strcmp(key, "password_expiry_utc")) {
>  			errno = 0;
>  			c->password_expiry_utc = parse_timestamp(value, NULL, 10);
> @@ -288,14 +327,18 @@ static void credential_write_item(FILE *fp, const char *key, const char *value,
>  	fprintf(fp, "%s=%s\n", key, value);
>  }
>  
> -void credential_write(const struct credential *c, FILE *fp)
> +void credential_write(const struct credential *c, FILE *fp, int op_type)
>  {
> +	if (credential_has_capability(&c->capa_authtype, op_type)) {
> +		credential_write_item(fp, "capability[]", "authtype", 0);
> +		credential_write_item(fp, "authtype", c->authtype, 0);
> +		credential_write_item(fp, "credential", c->credential, 0);
> +	}
>  	credential_write_item(fp, "protocol", c->protocol, 1);
>  	credential_write_item(fp, "host", c->host, 1);
>  	credential_write_item(fp, "path", c->path, 0);
>  	credential_write_item(fp, "username", c->username, 0);
>  	credential_write_item(fp, "password", c->password, 0);
> -	credential_write_item(fp, "credential", c->credential, 0);
>  	credential_write_item(fp, "oauth_refresh_token", c->oauth_refresh_token, 0);
>  	if (c->password_expiry_utc != TIME_MAX) {
>  		char *s = xstrfmt("%"PRItime, c->password_expiry_utc);
> @@ -304,7 +347,6 @@ void credential_write(const struct credential *c, FILE *fp)
>  	}
>  	for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
>  		credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
> -	credential_write_item(fp, "authtype", c->authtype, 0);
>  }
>  
>  static int run_credential_helper(struct credential *c,
> @@ -327,14 +369,14 @@ static int run_credential_helper(struct credential *c,
>  
>  	fp = xfdopen(helper.in, "w");
>  	sigchain_push(SIGPIPE, SIG_IGN);
> -	credential_write(c, fp);
> +	credential_write(c, fp, want_output ? CREDENTIAL_OP_HELPER : CREDENTIAL_OP_RESPONSE);
>  	fclose(fp);
>  	sigchain_pop(SIGPIPE);
>  
>  	if (want_output) {
>  		int r;
>  		fp = xfdopen(helper.out, "r");
> -		r = credential_read(c, fp);
> +		r = credential_read(c, fp, CREDENTIAL_OP_HELPER);
>  		fclose(fp);
>  		if (r < 0) {
>  			finish_command(&helper);
> @@ -367,7 +409,7 @@ static int credential_do(struct credential *c, const char *helper,
>  	return r;
>  }
>  
> -void credential_fill(struct credential *c)
> +void credential_fill(struct credential *c, int all_capabilities)
>  {
>  	int i;
>  
> @@ -375,6 +417,8 @@ void credential_fill(struct credential *c)
>  		return;
>  
>  	credential_apply_config(c);
> +	if (all_capabilities)
> +		credential_set_all_capabilities(c);
>  
>  	for (i = 0; i < c->helpers.nr; i++) {
>  		credential_do(c, c->helpers.items[i].string, "get");
> diff --git a/credential.h b/credential.h
> index 9db892cf4d..2051d04c5a 100644
> --- a/credential.h
> +++ b/credential.h
> @@ -93,6 +93,25 @@
>   * -----------------------------------------------------------------------
>   */
>  
> +/*
> + * These values define the kind of operation we're performing and the
> + * capabilities at each stage.  The first is either an external request (via git
> + * credential fill) or an internal request (e.g., via the HTTP) code.  The
> + * second is the call to the credential helper, and the third is the response
> + * we're providing.
> + *
> + * At each stage, we will emit the capability only if the previous stage
> + * supported it.
> + */
> +#define CREDENTIAL_OP_INITIAL  1
> +#define CREDENTIAL_OP_HELPER   2
> +#define CREDENTIAL_OP_RESPONSE 3

Is there any specific reason why you're using defines instead of an enum
here? I think the latter would be more self-explanatory when you see
that functions take `enum credential_op` as input instead of an `int`.

Patrick

> +struct credential_capability {
> +	unsigned request_initial:1,
> +		 request_helper:1,
> +		 response:1;
> +};
>  
>  /**
>   * This struct represents a single username/password combination
> @@ -136,6 +155,8 @@ struct credential {
>  		 use_http_path:1,
>  		 username_from_proto:1;
>  
> +	struct credential_capability capa_authtype;
> +
>  	char *username;
>  	char *password;
>  	char *credential;
> @@ -174,8 +195,11 @@ void credential_clear(struct credential *);
>   * returns, the username and password fields of the credential are
>   * guaranteed to be non-NULL. If an error occurs, the function will
>   * die().
> + *
> + * If all_capabilities is set, this is an internal user that is prepared
> + * to deal with all known capabilities, and we should advertise that fact.
>   */
> -void credential_fill(struct credential *);
> +void credential_fill(struct credential *, int all_capabilities);
>  
>  /**
>   * Inform the credential subsystem that the provided credentials
> @@ -198,8 +222,8 @@ void credential_approve(struct credential *);
>   */
>  void credential_reject(struct credential *);
>  
> -int credential_read(struct credential *, FILE *);
> -void credential_write(const struct credential *, FILE *);
> +int credential_read(struct credential *, FILE *, int);
> +void credential_write(const struct credential *, FILE *, int);
>  
>  /*
>   * Parse a url into a credential struct, replacing any existing contents.
> diff --git a/http.c b/http.c
> index 1c2200da77..4f5df6fb14 100644
> --- a/http.c
> +++ b/http.c
> @@ -569,7 +569,7 @@ static void init_curl_http_auth(CURL *result)
>  		return;
>  	}
>  
> -	credential_fill(&http_auth);
> +	credential_fill(&http_auth, 0);
>  
>  	curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
>  	curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
> @@ -596,7 +596,7 @@ static void init_curl_proxy_auth(CURL *result)
>  {
>  	if (proxy_auth.username) {
>  		if (!proxy_auth.password)
> -			credential_fill(&proxy_auth);
> +			credential_fill(&proxy_auth, 0);
>  		set_proxyauth_name_password(result);
>  	}
>  
> @@ -630,7 +630,7 @@ static int has_cert_password(void)
>  		cert_auth.host = xstrdup("");
>  		cert_auth.username = xstrdup("");
>  		cert_auth.path = xstrdup(ssl_cert);
> -		credential_fill(&cert_auth);
> +		credential_fill(&cert_auth, 0);
>  	}
>  	return 1;
>  }
> @@ -645,7 +645,7 @@ static int has_proxy_cert_password(void)
>  		proxy_cert_auth.host = xstrdup("");
>  		proxy_cert_auth.username = xstrdup("");
>  		proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
> -		credential_fill(&proxy_cert_auth);
> +		credential_fill(&proxy_cert_auth, 0);
>  	}
>  	return 1;
>  }
> @@ -2191,7 +2191,7 @@ static int http_request_reauth(const char *url,
>  		BUG("Unknown http_request target");
>  	}
>  
> -	credential_fill(&http_auth);
> +	credential_fill(&http_auth, 0);
>  
>  	return http_request(url, result, target, options);
>  }
> diff --git a/imap-send.c b/imap-send.c
> index f2e1947e63..8c89e866b6 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -944,7 +944,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
>  	cred->username = xstrdup_or_null(srvc->user);
>  	cred->password = xstrdup_or_null(srvc->pass);
>  
> -	credential_fill(cred);
> +	credential_fill(cred, 1);
>  
>  	if (!srvc->user)
>  		srvc->user = xstrdup(cred->username);
> diff --git a/remote-curl.c b/remote-curl.c
> index e37eaa17b7..f96bda2431 100644
> --- a/remote-curl.c
> +++ b/remote-curl.c
> @@ -926,7 +926,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
>  		do {
>  			err = probe_rpc(rpc, &results);
>  			if (err == HTTP_REAUTH)
> -				credential_fill(&http_auth);
> +				credential_fill(&http_auth, 0);
>  		} while (err == HTTP_REAUTH);
>  		if (err != HTTP_OK)
>  			return -1;
> @@ -1044,7 +1044,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
>  	rpc->any_written = 0;
>  	err = run_slot(slot, NULL);
>  	if (err == HTTP_REAUTH && !large_request) {
> -		credential_fill(&http_auth);
> +		credential_fill(&http_auth, 0);
>  		curl_slist_free_all(headers);
>  		goto retry;
>  	}
> diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
> index 400f6bdbca..8477108b28 100755
> --- a/t/t0300-credentials.sh
> +++ b/t/t0300-credentials.sh
> @@ -12,7 +12,13 @@ test_expect_success 'setup helper scripts' '
>  	IFS==
>  	while read key value; do
>  		echo >&2 "$whoami: $key=$value"
> -		eval "$key=$value"
> +		if test -z "${key%%*\[\]}"
> +		then
> +			key=${key%%\[\]}
> +			eval "$key=\"\$$key $value\""
> +		else
> +			eval "$key=$value"
> +		fi
>  	done
>  	IFS=$OIFS
>  	EOF
> @@ -35,6 +41,16 @@ test_expect_success 'setup helper scripts' '
>  	test -z "$pass" || echo password=$pass
>  	EOF
>  
> +	write_script git-credential-verbatim-cred <<-\EOF &&
> +	authtype=$1; shift
> +	credential=$1; shift
> +	. ./dump
> +	echo capability[]=authtype
> +	test -z "${capability##*authtype*}" || return
> +	test -z "$authtype" || echo authtype=$authtype
> +	test -z "$credential" || echo credential=$credential
> +	EOF
> +
>  	write_script git-credential-verbatim-with-expiry <<-\EOF &&
>  	user=$1; shift
>  	pass=$1; shift
> @@ -64,6 +80,26 @@ test_expect_success 'credential_fill invokes helper' '
>  	EOF
>  '
>  
> +test_expect_success 'credential_fill invokes helper with credential' '
> +	check fill "verbatim-cred Bearer token" <<-\EOF
> +	capability[]=authtype
> +	protocol=http
> +	host=example.com
> +	--
> +	capability[]=authtype
> +	authtype=Bearer
> +	credential=token
> +	protocol=http
> +	host=example.com
> +	--
> +	verbatim-cred: get
> +	verbatim-cred: capability[]=authtype
> +	verbatim-cred: protocol=http
> +	verbatim-cred: host=example.com
> +	EOF
> +'
> +
> +
>  test_expect_success 'credential_fill invokes multiple helpers' '
>  	check fill useless "verbatim foo bar" <<-\EOF
>  	protocol=http
> @@ -83,6 +119,42 @@ test_expect_success 'credential_fill invokes multiple helpers' '
>  	EOF
>  '
>  
> +test_expect_success 'credential_fill response does not get capabilities when helpers are incapable' '
> +	check fill useless "verbatim foo bar" <<-\EOF
> +	capability[]=authtype
> +	protocol=http
> +	host=example.com
> +	--
> +	protocol=http
> +	host=example.com
> +	username=foo
> +	password=bar
> +	--
> +	useless: get
> +	useless: capability[]=authtype
> +	useless: protocol=http
> +	useless: host=example.com
> +	verbatim: get
> +	verbatim: capability[]=authtype
> +	verbatim: protocol=http
> +	verbatim: host=example.com
> +	EOF
> +'
> +
> +test_expect_success 'credential_fill response does not get capabilities when caller is incapable' '
> +	check fill "verbatim-cred Bearer token" <<-\EOF
> +	protocol=http
> +	host=example.com
> +	--
> +	protocol=http
> +	host=example.com
> +	--
> +	verbatim-cred: get
> +	verbatim-cred: protocol=http
> +	verbatim-cred: host=example.com
> +	EOF
> +'
> +
>  test_expect_success 'credential_fill stops when we get a full response' '
>  	check fill "verbatim one two" "verbatim three four" <<-\EOF
>  	protocol=http
> @@ -99,6 +171,25 @@ test_expect_success 'credential_fill stops when we get a full response' '
>  	EOF
>  '
>  
> +test_expect_success 'credential_fill thinks a credential is a full response' '
> +	check fill "verbatim-cred Bearer token" "verbatim three four" <<-\EOF
> +	capability[]=authtype
> +	protocol=http
> +	host=example.com
> +	--
> +	capability[]=authtype
> +	authtype=Bearer
> +	credential=token
> +	protocol=http
> +	host=example.com
> +	--
> +	verbatim-cred: get
> +	verbatim-cred: capability[]=authtype
> +	verbatim-cred: protocol=http
> +	verbatim-cred: host=example.com
> +	EOF
> +'
> +
>  test_expect_success 'credential_fill continues through partial response' '
>  	check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
>  	protocol=http
> @@ -175,6 +266,20 @@ test_expect_success 'credential_fill passes along metadata' '
>  	EOF
>  '
>  
> +test_expect_success 'credential_fill produces no credential without capability' '
> +	check fill "verbatim-cred Bearer token" <<-\EOF
> +	protocol=http
> +	host=example.com
> +	--
> +	protocol=http
> +	host=example.com
> +	--
> +	verbatim-cred: get
> +	verbatim-cred: protocol=http
> +	verbatim-cred: host=example.com
> +	EOF
> +'
> +
>  test_expect_success 'credential_approve calls all helpers' '
>  	check approve useless "verbatim one two" <<-\EOF
>  	protocol=http
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

  reply	other threads:[~2024-03-27  8:02 UTC|newest]

Thread overview: 66+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-24  1:12 [PATCH 00/13] Support for arbitrary schemes in credentials brian m. carlson
2024-03-24  1:12 ` [PATCH 01/13] credential: add an authtype field brian m. carlson
2024-03-24  1:12 ` [PATCH 02/13] remote-curl: reset headers on new request brian m. carlson
2024-03-24  1:12 ` [PATCH 03/13] http: use new headers for each object request brian m. carlson
2024-03-27  8:02   ` Patrick Steinhardt
2024-03-24  1:12 ` [PATCH 04/13] credential: add a field for pre-encoded credentials brian m. carlson
2024-03-24  1:12 ` [PATCH 05/13] credential: gate new fields on capability brian m. carlson
2024-03-27  8:02   ` Patrick Steinhardt [this message]
2024-03-27 21:33     ` brian m. carlson
2024-04-02 10:04       ` Patrick Steinhardt
2024-04-04  0:39         ` brian m. carlson
2024-04-04  4:07           ` Patrick Steinhardt
2024-03-28 10:20   ` Jeff King
2024-03-28 16:13     ` Junio C Hamano
2024-03-28 16:29       ` Jeff King
2024-03-28 17:25         ` Junio C Hamano
2024-03-28 21:18     ` brian m. carlson
2024-03-24  1:12 ` [PATCH 06/13] docs: indicate new credential protocol fields brian m. carlson
2024-03-25 23:16   ` M Hickford
2024-03-25 23:37     ` brian m. carlson
2024-03-30 13:00       ` M Hickford
2024-03-31 21:43         ` brian m. carlson
2024-03-24  1:12 ` [PATCH 07/13] http: add support for authtype and credential brian m. carlson
2024-03-24  1:12 ` [PATCH 08/13] credential: add an argument to keep state brian m. carlson
2024-04-01 21:05   ` mirth hickford
2024-04-01 22:14     ` brian m. carlson
2024-03-24  1:12 ` [PATCH 09/13] credential: enable state capability brian m. carlson
2024-03-24  1:12 ` [PATCH 10/13] docs: set a limit on credential line length brian m. carlson
2024-03-24  1:12 ` [PATCH 11/13] t5563: refactor for multi-stage authentication brian m. carlson
2024-03-24  1:13 ` [PATCH 12/13] strvec: implement swapping two strvecs brian m. carlson
2024-03-27  8:02   ` Patrick Steinhardt
2024-03-27 21:22     ` Junio C Hamano
2024-03-27 21:34       ` brian m. carlson
2024-03-24  1:13 ` [PATCH 13/13] credential: add support for multistage credential rounds brian m. carlson
2024-03-28  8:00   ` M Hickford
2024-03-28 21:53     ` brian m. carlson
2024-04-01 20:51       ` M Hickford
2024-03-24  2:24 ` [PATCH 00/13] Support for arbitrary schemes in credentials Junio C Hamano
2024-03-24 15:21   ` brian m. carlson
2024-03-24 16:13     ` Junio C Hamano
2024-03-30  8:00 ` M Hickford
2024-03-30  8:16 ` M Hickford
2024-04-02 22:26 ` Calvin Wan
2024-04-04  1:01   ` brian m. carlson
2024-04-08 18:42     ` Jackson Toeniskoetter
2024-04-11  7:00       ` M Hickford
2024-04-12  0:09       ` brian m. carlson
2024-04-11  7:00 ` M Hickford
2024-04-12  0:13   ` brian m. carlson
2024-04-17  0:02 ` [PATCH v2 00/16] " brian m. carlson
2024-04-17  0:02   ` [PATCH v2 01/16] credential: add an authtype field brian m. carlson
2024-04-17  0:02   ` [PATCH v2 02/16] remote-curl: reset headers on new request brian m. carlson
2024-04-17  0:02   ` [PATCH v2 03/16] http: use new headers for each object request brian m. carlson
2024-04-17  0:02   ` [PATCH v2 04/16] credential: add a field for pre-encoded credentials brian m. carlson
2024-04-17  0:02   ` [PATCH v2 05/16] credential: gate new fields on capability brian m. carlson
2024-04-17  0:02   ` [PATCH v2 06/16] credential: add a field called "ephemeral" brian m. carlson
2024-04-17  0:02   ` [PATCH v2 07/16] docs: indicate new credential protocol fields brian m. carlson
2024-04-17  0:02   ` [PATCH v2 08/16] http: add support for authtype and credential brian m. carlson
2024-04-17  0:02   ` [PATCH v2 09/16] credential: add an argument to keep state brian m. carlson
2024-04-17  0:02   ` [PATCH v2 10/16] credential: enable state capability brian m. carlson
2024-04-17  0:02   ` [PATCH v2 11/16] docs: set a limit on credential line length brian m. carlson
2024-04-17  0:02   ` [PATCH v2 12/16] t5563: refactor for multi-stage authentication brian m. carlson
2024-04-17  0:02   ` [PATCH v2 13/16] credential: add support for multistage credential rounds brian m. carlson
2024-04-17  0:02   ` [PATCH v2 14/16] t: add credential tests for authtype brian m. carlson
2024-04-17  0:02   ` [PATCH v2 15/16] credential-cache: implement authtype capability brian m. carlson
2024-04-17  0:02   ` [PATCH v2 16/16] credential: add method for querying capabilities brian m. carlson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZgPSn_D8luXQyZGf@tanuki \
    --to=ps@pks.im \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=mirth.hickford@gmail.com \
    --cc=mjcheetham@outlook.com \
    --cc=sandals@crustytoothpaste.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).