Commit 7602cb7c0ea0d69efd30640af234be20393bf57c

Ben Straub 2013-01-31T10:44:57

Add user-from-url param to auth callback

diff --git a/examples/network/clone.c b/examples/network/clone.c
index 63072ee..5b0a810 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 7c213c8..e3eb91d 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 4945ff1..469b43f 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 8d8eb99..a05d5e8 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 e5bb107..1449064 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 b7f45c2..6994cc0 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 8f92cdd..5618347 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;