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". In other words, replace the internal "test-credential" by an official Git command. Most documentation writen by: Jeff King <peff@peff.net> Signed-off-by: Pavel Volek <Pavel.Volek@ensimag.imag.fr> Signed-off-by: Kim Thuat Nguyen <Kim-Thuat.Nguyen@ensimag.imag.fr> Signed-off-by: Javier Roucher Iglesias <Javier.Roucher-Iglesias@ensimag.imag.fr> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Javier Roucher Iglesias committed Jun 24, 2012 at 13:39 UTC e30b2feb1b50c2d14d32dc3e6e41f7b20a677ff2
8 files changed +163 -52
.gitignore
+1 -1
@@ -31,6 +31,7 @@
31 /git-commit-tree
32 /git-config
33 /git-count-objects
34 +/git-credential
35 /git-credential-cache
36 /git-credential-cache--daemon
37 /git-credential-store
@@ -172,7 +173,6 @@
173 /gitweb/static/gitweb.js
174 /gitweb/static/gitweb.min.*
175 /test-chmtime
175 -/test-credential
176 /test-ctype
177 /test-date
178 /test-delta
Documentation/git-credential.txt new
+136
@@ -0,0 +1,136 @@
1 +git-credential(1)
2 +=================
3 +
4 +NAME
5 +----
6 +git-credential - retrieve and store user credentials
7 +
8 +SYNOPSIS
9 +--------
10 +------------------
11 +git credential <fill|approve|reject>
12 +------------------
13 +
14 +DESCRIPTION
15 +-----------
16 +
17 +Git has an internal interface for storing and retrieving credentials
18 +from system-specific helpers, as well as prompting the user for
19 +usernames and passwords. The git-credential command exposes this
20 +interface to scripts which may want to retrieve, store, or prompt for
21 +credentials in the same manner as git. The design of this scriptable
22 +interface models the internal C API; see
23 +link:technical/api-credentials.txt[the git credential API] for more
24 +background on the concepts.
25 +
26 +git-credential takes an "action" option on the command-line (one of
27 +`fill`, `approve`, or `reject`) and reads a credential description
28 +on stdin (see <<IOFMT,INPUT/OUTPUT FORMAT>>).
29 +
30 +If the action is `fill`, git-credential will attempt to add "username"
31 +and "password" attributes to the description by reading config files,
32 +by contacting any configured credential helpers, or by prompting the
33 +user. The username and password attributes of the credential
34 +description are then printed to stdout together with the attributes
35 +already provided.
36 +
37 +If the action is `approve`, git-credential will send the description
38 +to any configured credential helpers, which may store the credential
39 +for later use.
40 +
41 +If the action is `reject`, git-credential will send the description to
42 +any configured credential helpers, which may erase any stored
43 +credential matching the description.
44 +
45 +If the action is `approve` or `reject`, no output should be emitted.
46 +
47 +TYPICAL USE OF GIT CREDENTIAL
48 +-----------------------------
49 +
50 +An application using git-credential will typically use `git
51 +credential` following these steps:
52 +
53 + 1. Generate a credential description based on the context.
54 ++
55 +For example, if we want a password for
56 +`https://example.com/foo.git`, we might generate the following
57 +credential description (don't forget the blank line at the end; it
58 +tells `git credential` that the application finished feeding all the
59 +infomation it has):
60 +
61 + protocol=https
62 + host=example.com
63 + path=foo.git
64 +
65 + 2. Ask git-credential to give us a username and password for this
66 + description. This is done by running `git credential fill`,
67 + feeding the description from step (1) to its standard input. The
68 + credential will be produced on standard output, like:
69 +
70 + username=bob
71 + password=secr3t
72 ++
73 +If the `git credential` knew about the password, this step may
74 +not have involved the user actually typing this password (the
75 +user may have typed a password to unlock the keychain instead,
76 +or no user interaction was done if the keychain was already
77 +unlocked) before it returned `password=secr3t`.
78 +
79 + 3. Use the credential (e.g., access the URL with the username and
80 + password from step (2)), and see if it's accepted.
81 +
82 + 4. Report on the success or failure of the password. If the
83 + credential allowed the operation to complete successfully, then
84 + it can be marked with an "approve" action to tell `git
85 + credential` to reuse it in its next invocation. If the credential
86 + was rejected during the operation, use the "reject" action so
87 + that `git credential` will ask for a new password in its next
88 + invocation. In either case, `git credential` should be fed with
89 + the credential description obtained from step (2) together with
90 + the ones already provided in step (1).
91 +
92 +[[IOFMT]]
93 +INPUT/OUTPUT FORMAT
94 +-------------------
95 +
96 +`git credential` reads and/or writes (depending on the action used)
97 +credential information in its standard input/output. These information
98 +can correspond either to keys for which `git credential` will obtain
99 +the login/password information (e.g. host, protocol, path), or to the
100 +actual credential data to be obtained (login/password).
101 +
102 +The credential is split into a set of named attributes.
103 +Attributes are provided to the helper, one per line. Each attribute is
104 +specified by a key-value pair, separated by an `=` (equals) sign,
105 +followed by a newline. The key may contain any bytes except `=`,
106 +newline, or NUL. The value may contain any bytes except newline or NUL.
107 +In both cases, all bytes are treated as-is (i.e., there is no quoting,
108 +and one cannot transmit a value with newline or NUL in it). The list of
109 +attributes is terminated by a blank line or end-of-file.
110 +Git will send the following attributes (but may not send all of
111 +them for a given credential; for example, a `host` attribute makes no
112 +sense when dealing with a non-network protocol):
113 +
114 +`protocol`::
115 +
116 + The protocol over which the credential will be used (e.g.,
117 + `https`).
118 +
119 +`host`::
120 +
121 + The remote hostname for a network credential.
122 +
123 +`path`::
124 +
125 + The path with which the credential will be used. E.g., for
126 + accessing a remote https repository, this will be the
127 + repository's path on the server.
128 +
129 +`username`::
130 +
131 + The credential's username, if we already have one (e.g., from a
132 + URL, from the user, or from a previously run helper).
133 +
134 +`password`::
135 +
136 + The credential's password, if we are asking it to be stored.
Documentation/technical/api-credentials.txt
+3 -36
@@ -241,42 +241,9 @@ appended to its command line, which is one of:
241 Remove a matching credential, if any, from the helper's storage.
242
243 The details of the credential will be provided on the helper's stdin
244 -stream. The credential is split into a set of named attributes.
245 -Attributes are provided to the helper, one per line. Each attribute is
246 -specified by a key-value pair, separated by an `=` (equals) sign,
247 -followed by a newline. The key may contain any bytes except `=`,
248 -newline, or NUL. The value may contain any bytes except newline or NUL.
249 -In both cases, all bytes are treated as-is (i.e., there is no quoting,
250 -and one cannot transmit a value with newline or NUL in it). The list of
251 -attributes is terminated by a blank line or end-of-file.
252 -
253 -Git will send the following attributes (but may not send all of
254 -them for a given credential; for example, a `host` attribute makes no
255 -sense when dealing with a non-network protocol):
256 -
257 -`protocol`::
258 -
259 - The protocol over which the credential will be used (e.g.,
260 - `https`).
261 -
262 -`host`::
263 -
264 - The remote hostname for a network credential.
265 -
266 -`path`::
267 -
268 - The path with which the credential will be used. E.g., for
269 - accessing a remote https repository, this will be the
270 - repository's path on the server.
271 -
272 -`username`::
273 -
274 - The credential's username, if we already have one (e.g., from a
275 - URL, from the user, or from a previously run helper).
276 -
277 -`password`::
278 -
279 - The credential's password, if we are asking it to be stored.
244 +stream. The exact format is the same as the input/output format of the
245 +`git credential` plumbing command (see the section `INPUT/OUTPUT
246 +FORMAT` in linkgit:git-credential[7] for a detailed specification).
247
248 For a `get` operation, the helper should produce a list of attributes
249 on stdout in the same format. A helper is free to produce a subset, or
Makefile
+1 -1
@@ -487,7 +487,6 @@ X =
487 PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
488
489 TEST_PROGRAMS_NEED_X += test-chmtime
490 -TEST_PROGRAMS_NEED_X += test-credential
490 TEST_PROGRAMS_NEED_X += test-ctype
491 TEST_PROGRAMS_NEED_X += test-date
492 TEST_PROGRAMS_NEED_X += test-delta
@@ -835,6 +834,7 @@ BUILTIN_OBJS += builtin/commit-tree.o
834 BUILTIN_OBJS += builtin/commit.o
835 BUILTIN_OBJS += builtin/config.o
836 BUILTIN_OBJS += builtin/count-objects.o
837 +BUILTIN_OBJS += builtin/credential.o
838 BUILTIN_OBJS += builtin/describe.o
839 BUILTIN_OBJS += builtin/diff-files.o
840 BUILTIN_OBJS += builtin/diff-index.o
builtin.h
+1
@@ -65,6 +65,7 @@ extern int cmd_commit(int argc, const char **argv, const char *prefix);
65 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
66 extern int cmd_config(int argc, const char **argv, const char *prefix);
67 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
68 +extern int cmd_credential(int argc, const char **argv, const char *prefix);
69 extern int cmd_describe(int argc, const char **argv, const char *prefix);
70 extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
71 extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
builtin/credential.c renamed
+8 -12
@@ -1,21 +1,18 @@
1 -#include "cache.h"
1 +#include "git-compat-util.h"
2 #include "credential.h"
3 -#include "string-list.h"
3 +#include "builtin.h"
4
5 static const char usage_msg[] =
6 -"test-credential <fill|approve|reject> [helper...]";
6 + "git credential [fill|approve|reject]";
7
8 -int main(int argc, const char **argv)
8 +int cmd_credential(int argc, const char **argv, const char *prefix)
9 {
10 const char *op;
11 struct credential c = CREDENTIAL_INIT;
12 - int i;
12
13 op = argv[1];
14 if (!op)
15 usage(usage_msg);
17 - for (i = 2; i < argc; i++)
18 - string_list_append(&c.helpers, argv[i]);
16
17 if (credential_read(&c, stdin) < 0)
18 die("unable to read credential from stdin");
@@ -26,13 +23,12 @@ int main(int argc, const char **argv)
23 printf("username=%s\n", c.username);
24 if (c.password)
25 printf("password=%s\n", c.password);
29 - }
30 - else if (!strcmp(op, "approve"))
26 + } else if (!strcmp(op, "approve")) {
27 credential_approve(&c);
32 - else if (!strcmp(op, "reject"))
28 + } else if (!strcmp(op, "reject")) {
29 credential_reject(&c);
34 - else
30 + } else {
31 usage(usage_msg);
36 -
32 + }
33 return 0;
34 }
git.c
+1
@@ -351,6 +351,7 @@ static void handle_internal_command(int argc, const char **argv)
351 { "commit-tree", cmd_commit_tree, RUN_SETUP },
352 { "config", cmd_config, RUN_SETUP_GENTLY },
353 { "count-objects", cmd_count_objects, RUN_SETUP },
354 + { "credential", cmd_credential, RUN_SETUP_GENTLY },
355 { "describe", cmd_describe, RUN_SETUP },
356 { "diff", cmd_diff },
357 { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
t/lib-credential.sh
+12 -2
@@ -4,10 +4,20 @@
4 # stdout and stderr should be provided on stdin,
5 # separated by "--".
6 check() {
7 + credential_opts=
8 + credential_cmd=$1
9 + shift
10 + for arg in "$@"; do
11 + credential_opts="$credential_opts -c credential.helper='$arg'"
12 + done
13 read_chunk >stdin &&
14 read_chunk >expect-stdout &&
15 read_chunk >expect-stderr &&
10 - test-credential "$@" <stdin >stdout 2>stderr &&
16 + if ! eval "git $credential_opts credential $credential_cmd <stdin >stdout 2>stderr"; then
17 + echo "git credential failed with code $?" &&
18 + cat stderr &&
19 + false
20 + fi &&
21 test_cmp expect-stdout stdout &&
22 test_cmp expect-stderr stderr
23 }
@@ -41,7 +51,7 @@ reject() {
51 echo protocol=$2
52 echo host=$3
53 echo username=$4
44 - ) | test-credential reject $1
54 + ) | git -c credential.helper=$1 credential reject
55 }
56
57 helper_test() {