diff --git a/examples/network/clone.c b/examples/network/clone.c index 63072eea0..5b0a81073 100644 --- a/examples/network/clone.c +++ b/examples/network/clone.c @@ -61,6 +61,7 @@ static void checkout_progress(const char *path, size_t cur, size_t tot, void *pa static int cred_acquire(git_cred **out, const char * UNUSED(url), + const char * UNUSED(username_from_url), unsigned int UNUSED(allowed_types), void * UNUSED(payload)) { diff --git a/include/git2/cred_helpers.h b/include/git2/cred_helpers.h index 7c213c8dd..e3eb91d6c 100644 --- a/include/git2/cred_helpers.h +++ b/include/git2/cred_helpers.h @@ -34,6 +34,8 @@ typedef struct git_cred_userpass_payload { * * @param cred The newly created credential object. * @param url The resource for which we are demanding a credential. + * @param username_from_url The username that was embedded in a "user@host" + * remote url, or NULL if not included. * @param allowed_types A bitmask stating which cred types are OK to return. * @param payload The payload provided when specifying this callback. (This is * interpreted as a `git_cred_userpass_payload*`.) @@ -41,6 +43,7 @@ typedef struct git_cred_userpass_payload { GIT_EXTERN(int) git_cred_userpass( git_cred **cred, const char *url, + const char *user_from_url, unsigned int allowed_types, void *payload); diff --git a/include/git2/transport.h b/include/git2/transport.h index 4945ff151..469b43f72 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -62,6 +62,8 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new( * * @param cred The newly created credential object. * @param url The resource for which we are demanding a credential. + * @param username_from_url The username that was embedded in a "user@host" + * remote url, or NULL if not included. * @param allowed_types A bitmask stating which cred types are OK to return. * @param payload The payload provided when specifying this callback. * @return 0 for success or an error code for failure @@ -69,6 +71,7 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new( typedef int (*git_cred_acquire_cb)( git_cred **cred, const char *url, + const char *username_from_url, unsigned int allowed_types, void *payload); diff --git a/src/transports/cred_helpers.c b/src/transports/cred_helpers.c index 8d8eb9990..a05d5e874 100644 --- a/src/transports/cred_helpers.c +++ b/src/transports/cred_helpers.c @@ -11,17 +11,34 @@ int git_cred_userpass( git_cred **cred, const char *url, + const char *user_from_url, unsigned int allowed_types, void *payload) { git_cred_userpass_payload *userpass = (git_cred_userpass_payload*)payload; + const char *effective_username = NULL; GIT_UNUSED(url); - if (!userpass || !userpass->username || !userpass->password) return -1; + if (!userpass || !userpass->password) return -1; + + /* Username resolution: a username can be passed with the URL, the + * credentials payload, or both. Here's what we do. + * + * | Payload | URL | Used | + * +-------------+----------+-----------+ + * | yes | no | payload | + * | yes | yes | payload | + * | no | yes | url | + * | no | no | FAIL | + */ + effective_username = userpass->username; + if (!userpass->username && user_from_url) + effective_username = user_from_url; + if (!effective_username) return -1; if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 || - git_cred_userpass_plaintext_new(cred, userpass->username, userpass->password) < 0) + git_cred_userpass_plaintext_new(cred, effective_username, userpass->password) < 0) return -1; return 0; diff --git a/src/transports/http.c b/src/transports/http.c index e5bb1071f..144906474 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -257,6 +257,7 @@ static int on_headers_complete(http_parser *parser) if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, + t->user_from_url, allowed_types, t->owner->cred_acquire_payload) < 0) return PARSE_ERROR_GENERIC; diff --git a/tests-clar/network/cred.c b/tests-clar/network/cred.c index b7f45c23b..6994cc0c3 100644 --- a/tests-clar/network/cred.c +++ b/tests-clar/network/cred.c @@ -6,14 +6,14 @@ void test_network_cred__stock_userpass_validates_args(void) { git_cred_userpass_payload payload = {0}; - cl_git_fail(git_cred_userpass(NULL, NULL, 0, NULL)); + cl_git_fail(git_cred_userpass(NULL, NULL, NULL, 0, NULL)); payload.username = "user"; - cl_git_fail(git_cred_userpass(NULL, NULL, 0, &payload)); + cl_git_fail(git_cred_userpass(NULL, NULL, NULL, 0, &payload)); payload.username = NULL; payload.username = "pass"; - cl_git_fail(git_cred_userpass(NULL, NULL, 0, &payload)); + cl_git_fail(git_cred_userpass(NULL, NULL, NULL, 0, &payload)); } void test_network_cred__stock_userpass_validates_that_method_is_allowed(void) @@ -21,7 +21,30 @@ void test_network_cred__stock_userpass_validates_that_method_is_allowed(void) git_cred *cred; git_cred_userpass_payload payload = {"user", "pass"}; - cl_git_fail(git_cred_userpass(&cred, NULL, 0, &payload)); - cl_git_pass(git_cred_userpass(&cred, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, &payload)); + cl_git_fail(git_cred_userpass(&cred, NULL, NULL, 0, &payload)); + cl_git_pass(git_cred_userpass(&cred, NULL, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, &payload)); + cred->free(cred); +} + +void test_network_cred__stock_userpass_properly_handles_username_in_url(void) +{ + git_cred *cred; + git_cred_userpass_plaintext *plain; + git_cred_userpass_payload payload = {"alice", "password"}; + + cl_git_pass(git_cred_userpass(&cred, NULL, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, &payload)); + plain = (git_cred_userpass_plaintext*)cred; + cl_assert_equal_s(plain->username, "alice"); + cred->free(cred); + + cl_git_pass(git_cred_userpass(&cred, NULL, "bob", GIT_CREDTYPE_USERPASS_PLAINTEXT, &payload)); + plain = (git_cred_userpass_plaintext*)cred; + cl_assert_equal_s(plain->username, "alice"); + cred->free(cred); + + payload.username = NULL; + cl_git_pass(git_cred_userpass(&cred, NULL, "bob", GIT_CREDTYPE_USERPASS_PLAINTEXT, &payload)); + plain = (git_cred_userpass_plaintext*)cred; + cl_assert_equal_s(plain->username, "bob"); cred->free(cred); } diff --git a/tests-clar/online/push.c b/tests-clar/online/push.c index 8f92cdd5e..56183473a 100644 --- a/tests-clar/online/push.c +++ b/tests-clar/online/push.c @@ -30,9 +30,15 @@ static git_oid _tag_tree; static git_oid _tag_blob; static git_oid _tag_lightweight; -static int cred_acquire_cb(git_cred **cred, const char *url, unsigned int allowed_types, void *payload) +static int cred_acquire_cb( + git_cred **cred, + const char *url, + const char *user_from_url, + unsigned int allowed_types, + void *payload) { GIT_UNUSED(url); + GIT_UNUSED(user_from_url); *((bool*)payload) = true;