threads / rfc / 30738

RFC patchcredentials helpers+remote helpers

Subject: [PATCH/RFC] credentials helpers+remote helpers

## tl;dr

3 messages between Jun 7, 2012 and Jun 7, 2012. Diffs are folded; open one to read it.

replies: 2people: 3as markdown or json

javier.roucher-iglesias@ensimag.imag.fr· Jun 7, 2012, 14:35 UTC · lore
From: Javier Roucher <jroucher@gmail.com>
Add "git credential" plumbing command

The credential API is in C, and not available to scripting languages. Expose the functionalities of the API by wrapping them into a new plumbing command "git credentials".

Signed-off-by: Pavel Volek <Pavel.Volek@ensimag.imag.fr>
Signed-off-by: NGUYEN Kim Thuat <Kim-Thuat.Nguyen@ensimag.imag.fr>
Signed-off-by: ROUCHER IGLESIAS Javier <roucherj@ensimag.imag.fr>
Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
---
 .gitignore                       |  1 +
 Documentation/git-credential.txt | 70 ++++++++++++++++++++++++++++++++++++++++
 Makefile                         |  1 +
 builtin.h                        |  1 +
 builtin/credential.c             | 40 +++++++++++++++++++++++
 git.c                            |  1 +
 6 files changed, 114 insertions(+)
 create mode 100644 Documentation/git-credential.txt
 create mode 100644 builtin/credential.c
Show changes to 6 files +114 −0

.gitignore, Documentation/git-credential.txt, Makefile, builtin.h, builtin/credential.c, git.c

diff --git a/.gitignore b/.gitignore
index bf66648..7d1d86e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@
 /git-commit-tree
 /git-config
 /git-count-objects
+/git-credential
 /git-credential-cache
 /git-credential-cache--daemon
 /git-credential-store
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
new file mode 100644
index 0000000..a6e1d0a
--- /dev/null
+++ b/Documentation/git-credential.txt
@@ -0,0 +1,70 @@
+git-credential(7)
+=================
+
+NAME
+----
+git-credential - Providing and storing user credentials to git
+
+SYNOPSIS
+--------
+------------------
+git credential [fill|approve|reject]
+
+------------------
+
+DESCRIPTION
+-----------
+
+Git-credential permits to save username, password, host, path and protocol.
+When you invoke git-credential, you can ask for a password, using the command
+'git credential fill'.
+Providing them by STDIN: 
+
+		username=admin\n 
+		protocol=[http|https]\n
+		host=localhost\n
+		path=/dir\n\n
+
+-If git-credential system, have the password already stored
+git-credential will answer by STDOUT:
+	
+		username=admin\n
+		password=*****\n
+
+-If it is not stored, git-credential will ask you to enter 
+the password:
+		
+		> Password for '[http|https]admin@localhost':
+
+Then if password is correct, you can store using command
+'git crendential approve' providing the structure, by STDIN.
+
+		username=admin\n 
+		password=*****\n
+		protocol=[http|https]\n
+		host=localhost\n
+		path=/dir\n\n
+
+If the password is refused, you can delete using command
+'git credential reject' providing the same structure.
+
+
+REQUESTING CREDENTIALS
+----------------------
+
+1. The 'git credential fill' makes the structure:
+		username=foo
+		password=****
+		protocol=[http|https]
+		localhost=url
+		path=/direction
+
+   with this structure it will be able to save your
+   credentials, and if the credential is allready stored,
+   it will fill the password.
+
+2. Then 'git credential approve' to store them.
+
+3. Otherwise, if the credential is not correct you can do
+  'git credential reject' to delete the credential.
+-------------------------------------------
diff --git a/Makefile b/Makefile
index 4592f1f..3f53da8 100644
--- a/Makefile
+++ b/Makefile
@@ -827,6 +827,7 @@ BUILTIN_OBJS += builtin/commit-tree.o
 BUILTIN_OBJS += builtin/commit.o
 BUILTIN_OBJS += builtin/config.o
 BUILTIN_OBJS += builtin/count-objects.o
+BUILTIN_OBJS += builtin/credential.o
 BUILTIN_OBJS += builtin/describe.o
 BUILTIN_OBJS += builtin/diff-files.o
 BUILTIN_OBJS += builtin/diff-index.o
diff --git a/builtin.h b/builtin.h
index 338f540..48feddc 100644
--- a/builtin.h
+++ b/builtin.h
@@ -66,6 +66,7 @@ extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_config(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
+extern int cmd_credential(int argc, const char **argv, const char *prefix);
 extern int cmd_describe(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
diff --git a/builtin/credential.c b/builtin/credential.c
new file mode 100644
index 0000000..89f976b
--- /dev/null
+++ b/builtin/credential.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include "cache.h"
+#include "credential.h"
+#include "string-list.h"
+
+static const char usage_msg[] =
+"credential <fill|approve|reject> [helper...]";
+
+void cmd_credential (int argc, char **argv, const char *prefix){
+	const char *op;
+	struct credential c = CREDENTIAL_INIT;
+	int i;
+
+	op = argv[1];
+	if (!op)
+		usage(usage_msg);
+
+	for (i = 2; i < argc; i++)
+		string_list_append(&c.helpers, argv[i]);
+
+	if (credential_read(&c, stdin) < 0)
+		die("unable to read credential from stdin");
+
+	if (!strcmp(op, "fill")) {
+		credential_fill(&c);
+		if (c.username)
+			printf("username=%s\n", c.username);
+		if (c.password)
+			printf("password=%s\n", c.password);
+	}
+	else if (!strcmp(op, "approve")) {
+		credential_approve(&c);
+	}
+	else if (!strcmp(op, "reject")) {
+		credential_reject(&c);
+	}
+	else
+		usage(usage_msg);
+}
+
diff --git a/git.c b/git.c
index d232de9..211f01f 100644
--- a/git.c
+++ b/git.c
@@ -353,6 +353,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
 		{ "config", cmd_config, RUN_SETUP_GENTLY },
 		{ "count-objects", cmd_count_objects, RUN_SETUP },
+		{ "credential", cmd_credential, RUN_SETUP },
 		{ "describe", cmd_describe, RUN_SETUP },
 		{ "diff", cmd_diff },
 		{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
-- 
1.7.11.rc0.5.ge0c9cc0.dirty
Matthieu Moy· Jun 7, 2012, 17:13 UTC · re: javier.roucher-iglesias@ensimag.imag.fr · lore

Re: [PATCH/RFC] credentials helpers+remote helpers

> Subject: Re: [PATCH/RFC] credentials helpers+remote helpers

This is not a good title. The one you have below (which I suggested off-list) should be the first line of the patch.

Javier.Roucher-Iglesias@ensimag.imag.fr writes:
> Add "git credential" plumbing command
(I mean this one)
Show 6 quoted lines
>  .gitignore                       |  1 +
>  Documentation/git-credential.txt | 70 ++++++++++++++++++++++++++++++++++++++++
>  Makefile                         |  1 +
>  builtin.h                        |  1 +
>  builtin/credential.c             | 40 +++++++++++++++++++++++
>  git.c                            |  1 +
No tests?

Since this new command is essentially a copy-paste of test-credential.c, it would indeed make sense to replace test-credential calls in the scripts by calls to this "git credential" command.

> index 0000000..a6e1d0a
> --- /dev/null
> +++ b/Documentation/git-credential.txt

This file is not valid asciidoc. Please, run "make doc" and see if the generated HTML is right. Currently, it says

    SUBDIR ../
make[1]: `GIT-VERSION-FILE' is up to date.
    ASCIIDOC git-credential.html
asciidoc: ERROR: git-credential.txt: line 69: [blockdef-listing] missing closing delimiter
make: *** [git-credential.html] Error 1
(didn't I already mention it off-list?)
> +------------------
> +git credential [fill|approve|reject]
> +
> +------------------
[...]
> +static const char usage_msg[] =
> +"credential <fill|approve|reject> [helper...]";
Which one is right?

I already suggested several times that you get rid of this "helper" argument (inherited from test-credential), or that you give it a better API (e.g. --helper HELPER, but not positional argument). If this argument is usefull, then keep it but it should be documented properly.

> +Git-credential permits to save username, password, host, path and protocol.
> +When you invoke git-credential, you can ask for a password, using the command
> +'git credential fill'.

Who is "you" here? This "you" is the developer writing a script using "git credential", while this one :

> +-If it is not stored, git-credential will ask you to enter 
> +the password:
> +		
> +		> Password for '[http|https]admin@localhost':

... is the user of the script. Please be more precise, e.g. "a script can ask ...", "the user will be prompted for a password".

> +Then if password is correct, you can store using command
if _the_ password ... you can store _it_ using ...
> +If the password is refused, you can delete using command
delete _it_
-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/
Junio C Hamano· Jun 7, 2012, 19:22 UTC · re: javier.roucher-iglesias@ensimag.imag.fr · lore

Re: [PATCH/RFC] credentials helpers+remote helpers

Javier.Roucher-Iglesias@ensimag.imag.fr writes:
Show 13 quoted lines
> From: Javier Roucher <jroucher@gmail.com>
>
>
> Add "git credential" plumbing command
>
> The credential API is in C, and not available to scripting languages.
> Expose the functionalities of the API by wrapping them into a new
> plumbing command "git credentials".
>
> Signed-off-by: Pavel Volek <Pavel.Volek@ensimag.imag.fr>
> Signed-off-by: NGUYEN Kim Thuat <Kim-Thuat.Nguyen@ensimag.imag.fr>
> Signed-off-by: ROUCHER IGLESIAS Javier <roucherj@ensimag.imag.fr>
> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
In addition to all good comments already given by Matthieu,...
Show 12 quoted lines
> diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
> new file mode 100644
> index 0000000..a6e1d0a
> --- /dev/null
> +++ b/Documentation/git-credential.txt
> @@ -0,0 +1,70 @@
> +git-credential(7)
> +=================
> +
> +NAME
> +----
> +git-credential - Providing and storing user credentials to git

This sounds as if we are storing passwords "in git", which is not exactly the point of the credential API, no?

Show 19 quoted lines
> +SYNOPSIS
> +--------
> +------------------
> +git credential [fill|approve|reject]
> +
> +------------------
> +
> +DESCRIPTION
> +-----------
> +
> +Git-credential permits to save username, password, host, path and protocol.
> +When you invoke git-credential, you can ask for a password, using the command
> +'git credential fill'.
> +Providing them by STDIN: 
> +
> +		username=admin\n 
> +		protocol=[http|https]\n
> +		host=localhost\n
> +		path=/dir\n\n

It's a bit strange way to convey that the user feeds record separated by a blank line, and each column in a record is terminated with a newline.

How about saying that more explicitly? E.g. "when taking data from the standard input, the program treats each line as a separate data item, and the end of series of data item is signalled by a blank line" or something?

Show 5 quoted lines
> +-If git-credential system, have the password already stored
> +git-credential will answer by STDOUT:
> +	
> +		username=admin\n
> +		password=*****\n

Does the reading side get any clue that there is no more output, like you gave yourself on the input side (i.e. it can and should read until it sees a blank line)?

Shouldn't it?
Show 16 quoted lines
> +-If it is not stored, git-credential will ask you to enter 
> +the password:
> +		
> +		> Password for '[http|https]admin@localhost':
> +
> +Then if password is correct, you can store using command
> +'git crendential approve' providing the structure, by STDIN.
> +
> +		username=admin\n 
> +		password=*****\n
> +		protocol=[http|https]\n
> +		host=localhost\n
> +		path=/dir\n\n
> +
> +If the password is refused, you can delete using command
> +'git credential reject' providing the same structure.
It is unclear who decides "correct" vs "refused" here.

Perhaps it would help to describe the purpose of the script that uses this command first. My understanding is that there are three actors: the end user, the script that uses "git credential" and an external system that wants to authenticate the user.

    _
   / \        +------------------+      +-----------------+
  | U |       |                  |      |                 |
   \ /        | Script that uses |      | External system |
  --+--  <==> | "git credential" | <==> |                 | 
    ^         +------------------+      +-----------------+
   / \                 ^
                       |
                       v
                credential API

And the "Script" is trying to respond to the external system with credential material on behalf of the user. For that, if the script knows the username, it can give the <user,proto,host,path> tuple to "git credential", and if "git credential" knows the password, it will be given to the script. If it does not, it may ask the user and obtain it before giving it back to the script.

Is that what is going on?

Assuming it is, after that happens, the script gives the credential information to the external system. The external system may or may not accept that credential, and that is what decides "correct" vs "refused".

After that, the script tells the "git credential" the result; giving "reject" to it to purge the credential information that it already knows the external system will reject, for example.

← back to recent threads