From: Javier Roucher Iglesias <Javier.Roucher-Iglesias@xxxxxxxxxxxxxxx> 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@xxxxxxxx> Signed-off-by: Pavel Volek <Pavel.Volek@xxxxxxxxxxxxxxx> Signed-off-by: Kim Thuat Nguyen <Kim-Thuat.Nguyen@xxxxxxxxxxxxxxx> Signed-off-by: Javier Roucher Iglesias <Javier.Roucher-Iglesias@xxxxxxxxxxxxxxx> Signed-off-by: Matthieu Moy <Matthieu.Moy@xxxxxxx> --- .gitignore | 2 +- Documentation/git-credential.txt | 126 ++++++++++++++++++++++++++++ Documentation/technical/api-credentials.txt | 39 +-------- Makefile | 2 +- builtin.h | 1 + test-credential.c => builtin/credential.c | 20 ++--- git.c | 1 + t/lib-credential.sh | 14 +++- 8 files changed, 153 insertions(+), 52 deletions(-) create mode 100644 Documentation/git-credential.txt rename test-credential.c => builtin/credential.c (63%) diff --git a/.gitignore b/.gitignore index bf66648..c188d0b 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 @@ -172,7 +173,6 @@ /gitweb/static/gitweb.js /gitweb/static/gitweb.min.* /test-chmtime -/test-credential /test-ctype /test-date /test-delta diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt new file mode 100644 index 0000000..b64ac30 --- /dev/null +++ b/Documentation/git-credential.txt @@ -0,0 +1,126 @@ +git-credential(7) +================= + +NAME +---- +git-credential - retrieve and store user credentials + +SYNOPSIS +-------- +------------------ +git credential <fill|approve|reject> +------------------ + +DESCRIPTION +----------- + +Git has an internal interface for storing and retrieving credentials +from system-specific helpers, as well as prompting the user for +usernames and passwords. The git-credential command exposes this +interface to scripts which may want to retrieve, store, or prompt for +credentials in the same manner as git. The design of this scriptable +interface models the internal C API; see +link:technical/api-credentials.txt[the git credential API] for more +background on the concepts. + +git-credential takes an "action" option on the command-line (one of +`fill`, `approve`, or `reject`) and reads a credential description +on stdin (see <<IOFMT,INPUT/OUTPUT FORMAT>>). + +If the action is `fill`, git-credential will attempt to add "username" +and "password" attributes to the description by reading config files, +by contacting any configured credential helpers, or by prompting the +user. The username and password attributes of the credential +description are then printed to stdout together with the attributes +already provided. + +If the action is `approve`, git-credential will send the description +to any configured credential helpers, which may store the credential +for later use. + +If the action is `reject`, git-credential will send the description to +any configured credential helpers, which may erase any stored +credential matching the description. + +If the action is `approve` or `reject`, no output should be emitted. + +TYPICAL WORKFLOW +---------------- + +An application using git-credential will typically follow this +workflow: + + 1. Generate a credential description based on the context. ++ +For example, if we want a password for +`https://example.com/foo.git`, we might generate the following +credential description (don't forget the blank line at the end): + + protocol=https + host=example.com + path=foo.git + + 2. Ask git-credential to give us a username and password for this + description. This is done by running `git credential fill`, + feeding the description from step (1) to its stdin. The username + and password will be produced on stdout, like: + + username=bob + password=secr3t + + 3. Try to use the credential (e.g., by accessing the URL with the + username and password from step (2)). + + 4. Report on the success or failure of the password. If the + credential allowed the operation to complete successfully, then + it can be marked with an "approve" action. If the credential was + rejected during the operation, use the "reject" action. In either + case, `git credential` should be fed with the credential + description obtained from step (2) together with the ones already + provided in step (1). + +[[IOFMT]] +INPUT/OUTPUT FORMAT +------------------- + +`git credential` reads and/or writes (depending on the action used) +credential information in its standard input/output. These information +can correspond either to keys from which `git credential` will obtain +the login/password information (e.g. host, protocol, path), or to the +actual credential data to be obtained (login/password). + +The credential is split into a set of named attributes. +Attributes are provided to the helper, one per line. Each attribute is +specified by a key-value pair, separated by an `=` (equals) sign, +followed by a newline. The key may contain any bytes except `=`, +newline, or NUL. The value may contain any bytes except newline or NUL. +In both cases, all bytes are treated as-is (i.e., there is no quoting, +and one cannot transmit a value with newline or NUL in it). The list of +attributes is terminated by a blank line or end-of-file. +Git will send the following attributes (but may not send all of +them for a given credential; for example, a `host` attribute makes no +sense when dealing with a non-network protocol): + +`protocol`:: + + The protocol over which the credential will be used (e.g., + `https`). + +`host`:: + + The remote hostname for a network credential. + +`path`:: + + The path with which the credential will be used. E.g., for + accessing a remote https repository, this will be the + repository's path on the server. + +`username`:: + + The credential's username, if we already have one (e.g., from a + URL, from the user, or from a previously run helper). + +`password`:: + + The credential's password, if we are asking it to be stored. diff --git a/Documentation/technical/api-credentials.txt b/Documentation/technical/api-credentials.txt index adb6f0c..5977b58 100644 --- a/Documentation/technical/api-credentials.txt +++ b/Documentation/technical/api-credentials.txt @@ -241,42 +241,9 @@ appended to its command line, which is one of: Remove a matching credential, if any, from the helper's storage. The details of the credential will be provided on the helper's stdin -stream. The credential is split into a set of named attributes. -Attributes are provided to the helper, one per line. Each attribute is -specified by a key-value pair, separated by an `=` (equals) sign, -followed by a newline. The key may contain any bytes except `=`, -newline, or NUL. The value may contain any bytes except newline or NUL. -In both cases, all bytes are treated as-is (i.e., there is no quoting, -and one cannot transmit a value with newline or NUL in it). The list of -attributes is terminated by a blank line or end-of-file. - -Git will send the following attributes (but may not send all of -them for a given credential; for example, a `host` attribute makes no -sense when dealing with a non-network protocol): - -`protocol`:: - - The protocol over which the credential will be used (e.g., - `https`). - -`host`:: - - The remote hostname for a network credential. - -`path`:: - - The path with which the credential will be used. E.g., for - accessing a remote https repository, this will be the - repository's path on the server. - -`username`:: - - The credential's username, if we already have one (e.g., from a - URL, from the user, or from a previously run helper). - -`password`:: - - The credential's password, if we are asking it to be stored. +stream. The exact format is the same as the input/output format of the +`git credential` plumbing command (see the section `INPUT/OUTPUT +FORMAT` in linkgit:git-credential[7] for a detailed specification). For a `get` operation, the helper should produce a list of attributes on stdout in the same format. A helper is free to produce a subset, or diff --git a/Makefile b/Makefile index 4592f1f..670366b 100644 --- a/Makefile +++ b/Makefile @@ -480,7 +480,6 @@ X = PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS)) TEST_PROGRAMS_NEED_X += test-chmtime -TEST_PROGRAMS_NEED_X += test-credential TEST_PROGRAMS_NEED_X += test-ctype TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta @@ -827,6 +826,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/test-credential.c b/builtin/credential.c similarity index 63% rename from test-credential.c rename to builtin/credential.c index dee200e..4147314 100644 --- a/test-credential.c +++ b/builtin/credential.c @@ -1,21 +1,18 @@ -#include "cache.h" +#include <stdio.h> #include "credential.h" -#include "string-list.h" +#include "builtin.h" static const char usage_msg[] = -"test-credential <fill|approve|reject> [helper...]"; + "credential <fill|approve|reject>"; -int main(int argc, const char **argv) +int cmd_credential (int argc, const 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"); @@ -26,13 +23,12 @@ int main(int argc, const char **argv) printf("username=%s\n", c.username); if (c.password) printf("password=%s\n", c.password); - } - else if (!strcmp(op, "approve")) + } else if (!strcmp(op, "approve")) { credential_approve(&c); - else if (!strcmp(op, "reject")) + } else if (!strcmp(op, "reject")) { credential_reject(&c); - else + } else { usage(usage_msg); - + } return 0; } diff --git a/git.c b/git.c index d232de9..660c926 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_GENTLY }, { "describe", cmd_describe, RUN_SETUP }, { "diff", cmd_diff }, { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE }, diff --git a/t/lib-credential.sh b/t/lib-credential.sh index 4a37cd7..7c4826e 100755 --- a/t/lib-credential.sh +++ b/t/lib-credential.sh @@ -4,10 +4,20 @@ # stdout and stderr should be provided on stdin, # separated by "--". check() { + credential_opts= + credential_cmd=$1 + shift + for arg in "$@"; do + credential_opts="$credential_opts -c credential.helper='$arg'" + done read_chunk >stdin && read_chunk >expect-stdout && read_chunk >expect-stderr && - test-credential "$@" <stdin >stdout 2>stderr && + if ! eval "git $credential_opts credential $credential_cmd <stdin >stdout 2>stderr"; then + echo "git credential failed with code $?" && + cat stderr && + false + fi && test_cmp expect-stdout stdout && test_cmp expect-stderr stderr } @@ -41,7 +51,7 @@ reject() { echo protocol=$2 echo host=$3 echo username=$4 - ) | test-credential reject $1 + ) | git -c credential.helper=$1 credential reject } helper_test() { -- 1.7.11.5.g0c7e058.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html