Merge branch 'lm/credential-netrc'
Update credential-netrc helper (in contrib/) to allow customizing the GPG used to decrypt the encrypted .netrc file. * lm/credential-netrc: git-credential-netrc: accept gpg option git-credential-netrc: adapt to test framework for git
This commit is contained in:
commit
017b7c52fc
@ -1,5 +1,5 @@
|
|||||||
test:
|
test:
|
||||||
./test.pl
|
./t-git-credential-netrc.sh
|
||||||
|
|
||||||
testverbose:
|
testverbose:
|
||||||
./test.pl -d -v
|
./t-git-credential-netrc.sh -d -v
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
use autodie;
|
||||||
|
|
||||||
use Getopt::Long;
|
use Getopt::Long;
|
||||||
use File::Basename;
|
use File::Basename;
|
||||||
|
use Git;
|
||||||
|
|
||||||
my $VERSION = "0.1";
|
my $VERSION = "0.2";
|
||||||
|
|
||||||
my %options = (
|
my %options = (
|
||||||
help => 0,
|
help => 0,
|
||||||
@ -54,6 +56,7 @@ GetOptions(\%options,
|
|||||||
"insecure|k",
|
"insecure|k",
|
||||||
"verbose|v",
|
"verbose|v",
|
||||||
"file|f=s@",
|
"file|f=s@",
|
||||||
|
'gpg|g:s',
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($options{help}) {
|
if ($options{help}) {
|
||||||
@ -62,27 +65,31 @@ if ($options{help}) {
|
|||||||
|
|
||||||
print <<EOHIPPUS;
|
print <<EOHIPPUS;
|
||||||
|
|
||||||
$0 [-f AUTHFILE1] [-f AUTHFILEN] [-d] [-v] [-k] get
|
$0 [(-f <authfile>)...] [-g <program>] [-d] [-v] [-k] get
|
||||||
|
|
||||||
Version $VERSION by tzz\@lifelogs.com. License: BSD.
|
Version $VERSION by tzz\@lifelogs.com. License: BSD.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
-f|--file AUTHFILE : specify netrc-style files. Files with the .gpg extension
|
-f|--file <authfile>: specify netrc-style files. Files with the .gpg
|
||||||
will be decrypted by GPG before parsing. Multiple -f
|
extension will be decrypted by GPG before parsing.
|
||||||
arguments are OK. They are processed in order, and the
|
Multiple -f arguments are OK. They are processed in
|
||||||
first matching entry found is returned via the credential
|
order, and the first matching entry found is returned
|
||||||
helper protocol (see below).
|
via the credential helper protocol (see below).
|
||||||
|
|
||||||
When no -f option is given, .authinfo.gpg, .netrc.gpg,
|
When no -f option is given, .authinfo.gpg, .netrc.gpg,
|
||||||
.authinfo, and .netrc files in your home directory are used
|
.authinfo, and .netrc files in your home directory are
|
||||||
in this order.
|
used in this order.
|
||||||
|
|
||||||
-k|--insecure : ignore bad file ownership or permissions
|
-g|--gpg <program> : specify the program for GPG. By default, this is the
|
||||||
|
value of gpg.program in the git repository or global
|
||||||
|
option or gpg.
|
||||||
|
|
||||||
-d|--debug : turn on debugging (developer info)
|
-k|--insecure : ignore bad file ownership or permissions
|
||||||
|
|
||||||
-v|--verbose : be more verbose (show files and information found)
|
-d|--debug : turn on debugging (developer info)
|
||||||
|
|
||||||
|
-v|--verbose : be more verbose (show files and information found)
|
||||||
|
|
||||||
To enable this credential helper:
|
To enable this credential helper:
|
||||||
|
|
||||||
@ -99,8 +106,9 @@ in the path.)
|
|||||||
|
|
||||||
git config credential.helper '$shortname -f AUTHFILE -v'
|
git config credential.helper '$shortname -f AUTHFILE -v'
|
||||||
|
|
||||||
Only "get" mode is supported by this credential helper. It opens every AUTHFILE
|
Only "get" mode is supported by this credential helper. It opens every
|
||||||
and looks for the first entry that matches the requested search criteria:
|
<authfile> and looks for the first entry that matches the requested search
|
||||||
|
criteria:
|
||||||
|
|
||||||
'port|protocol':
|
'port|protocol':
|
||||||
The protocol that will be used (e.g., https). (protocol=X)
|
The protocol that will be used (e.g., https). (protocol=X)
|
||||||
@ -120,7 +128,7 @@ host=github.com
|
|||||||
protocol=https
|
protocol=https
|
||||||
username=tzz
|
username=tzz
|
||||||
|
|
||||||
this credential helper will look for the first entry in every AUTHFILE that
|
this credential helper will look for the first entry in every <authfile> that
|
||||||
matches
|
matches
|
||||||
|
|
||||||
machine github.com port https login tzz
|
machine github.com port https login tzz
|
||||||
@ -137,8 +145,8 @@ Then, the helper will print out whatever tokens it got from the entry, including
|
|||||||
back to "protocol". Any redundant entry tokens (part of the original query) are
|
back to "protocol". Any redundant entry tokens (part of the original query) are
|
||||||
skipped.
|
skipped.
|
||||||
|
|
||||||
Again, note that only the first matching entry from all the AUTHFILEs, processed
|
Again, note that only the first matching entry from all the <authfile>s,
|
||||||
in the sequence given on the command line, is used.
|
processed in the sequence given on the command line, is used.
|
||||||
|
|
||||||
Netrc/authinfo tokens can be quoted as 'STRING' or "STRING".
|
Netrc/authinfo tokens can be quoted as 'STRING' or "STRING".
|
||||||
|
|
||||||
@ -152,7 +160,7 @@ EOHIPPUS
|
|||||||
my $mode = shift @ARGV;
|
my $mode = shift @ARGV;
|
||||||
|
|
||||||
# Credentials must get a parameter, so die if it's missing.
|
# Credentials must get a parameter, so die if it's missing.
|
||||||
die "Syntax: $0 [-f AUTHFILE1] [-f AUTHFILEN] [-d] get" unless defined $mode;
|
die "Syntax: $0 [(-f <authfile>)...] [-d] get" unless defined $mode;
|
||||||
|
|
||||||
# Only support 'get' mode; with any other unsupported ones we just exit.
|
# Only support 'get' mode; with any other unsupported ones we just exit.
|
||||||
exit 0 unless $mode eq 'get';
|
exit 0 unless $mode eq 'get';
|
||||||
@ -172,6 +180,8 @@ unless (scalar @$files) {
|
|||||||
$files = $options{file} = [ map { glob $_ } @candidates ];
|
$files = $options{file} = [ map { glob $_ } @candidates ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load_config(\%options);
|
||||||
|
|
||||||
my $query = read_credential_data_from_stdin();
|
my $query = read_credential_data_from_stdin();
|
||||||
|
|
||||||
FILE:
|
FILE:
|
||||||
@ -233,7 +243,7 @@ sub load_netrc {
|
|||||||
|
|
||||||
my $io;
|
my $io;
|
||||||
if ($gpgmode) {
|
if ($gpgmode) {
|
||||||
my @cmd = (qw(gpg --decrypt), $file);
|
my @cmd = ($options{'gpg'}, qw(--decrypt), $file);
|
||||||
log_verbose("Using GPG to open $file: [@cmd]");
|
log_verbose("Using GPG to open $file: [@cmd]");
|
||||||
open $io, "-|", @cmd;
|
open $io, "-|", @cmd;
|
||||||
} else {
|
} else {
|
||||||
@ -410,6 +420,14 @@ sub print_credential_data {
|
|||||||
printf "%s=%s\n", $git_token, $entry->{$git_token};
|
printf "%s=%s\n", $git_token, $entry->{$git_token};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sub load_config {
|
||||||
|
# load settings from git config
|
||||||
|
my $options = shift;
|
||||||
|
# set from command argument, gpg.program option, or default to gpg
|
||||||
|
$options->{'gpg'} //= Git->repository()->config('gpg.program')
|
||||||
|
// 'gpg';
|
||||||
|
log_verbose("using $options{'gpg'} for GPG operations");
|
||||||
|
}
|
||||||
sub log_verbose {
|
sub log_verbose {
|
||||||
return unless $options{verbose};
|
return unless $options{verbose};
|
||||||
printf STDERR @_;
|
printf STDERR @_;
|
||||||
|
31
contrib/credential/netrc/t-git-credential-netrc.sh
Executable file
31
contrib/credential/netrc/t-git-credential-netrc.sh
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
(
|
||||||
|
cd ../../../t
|
||||||
|
test_description='git-credential-netrc'
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
if ! test_have_prereq PERL; then
|
||||||
|
skip_all='skipping perl interface tests, perl not available'
|
||||||
|
test_done
|
||||||
|
fi
|
||||||
|
|
||||||
|
perl -MTest::More -e 0 2>/dev/null || {
|
||||||
|
skip_all="Perl Test::More unavailable, skipping test"
|
||||||
|
test_done
|
||||||
|
}
|
||||||
|
|
||||||
|
# set up test repository
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'set up test repository' \
|
||||||
|
'git config --add gpg.program test.git-config-gpg'
|
||||||
|
|
||||||
|
# The external test will outputs its own plan
|
||||||
|
test_external_has_tap=1
|
||||||
|
|
||||||
|
test_external \
|
||||||
|
'git-credential-netrc' \
|
||||||
|
perl "$TEST_DIRECTORY"/../contrib/credential/netrc/test.pl
|
||||||
|
|
||||||
|
test_done
|
||||||
|
)
|
2
contrib/credential/netrc/test.command-option-gpg
Executable file
2
contrib/credential/netrc/test.command-option-gpg
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo machine command-option-gpg login username password password
|
2
contrib/credential/netrc/test.git-config-gpg
Executable file
2
contrib/credential/netrc/test.git-config-gpg
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo machine git-config-gpg login username password password
|
0
contrib/credential/netrc/test.netrc.gpg
Normal file
0
contrib/credential/netrc/test.netrc.gpg
Normal file
@ -1,83 +1,115 @@
|
|||||||
#!/usr/bin/perl
|
#!/usr/bin/perl
|
||||||
|
use lib (split(/:/, $ENV{GITPERLLIB}));
|
||||||
|
|
||||||
use warnings;
|
use warnings;
|
||||||
use strict;
|
use strict;
|
||||||
use Test;
|
use Test::More qw(no_plan);
|
||||||
|
use File::Basename;
|
||||||
|
use File::Spec::Functions qw(:DEFAULT rel2abs);
|
||||||
use IPC::Open2;
|
use IPC::Open2;
|
||||||
|
|
||||||
BEGIN { plan tests => 15 }
|
BEGIN {
|
||||||
|
# t-git-credential-netrc.sh kicks off our testing, so we have to go
|
||||||
|
# from there.
|
||||||
|
Test::More->builder->current_test(1);
|
||||||
|
Test::More->builder->no_ending(1);
|
||||||
|
}
|
||||||
|
|
||||||
my @global_credential_args = @ARGV;
|
my @global_credential_args = @ARGV;
|
||||||
my $netrc = './test.netrc';
|
my $scriptDir = dirname rel2abs $0;
|
||||||
print "# Testing insecure file, nothing should be found\n";
|
my ($netrc, $netrcGpg, $gcNetrc) = map { catfile $scriptDir, $_; }
|
||||||
|
qw(test.netrc
|
||||||
|
test.netrc.gpg
|
||||||
|
git-credential-netrc);
|
||||||
|
local $ENV{PATH} = join ':'
|
||||||
|
, $scriptDir
|
||||||
|
, $ENV{PATH}
|
||||||
|
? $ENV{PATH}
|
||||||
|
: ();
|
||||||
|
|
||||||
|
diag "Testing insecure file, nothing should be found\n";
|
||||||
chmod 0644, $netrc;
|
chmod 0644, $netrc;
|
||||||
my $cred = run_credential(['-f', $netrc, 'get'],
|
my $cred = run_credential(['-f', $netrc, 'get'],
|
||||||
{ host => 'github.com' });
|
{ host => 'github.com' });
|
||||||
|
|
||||||
ok(scalar keys %$cred, 0, "Got 0 keys from insecure file");
|
ok(scalar keys %$cred == 0, "Got 0 keys from insecure file");
|
||||||
|
|
||||||
print "# Testing missing file, nothing should be found\n";
|
diag "Testing missing file, nothing should be found\n";
|
||||||
chmod 0644, $netrc;
|
chmod 0644, $netrc;
|
||||||
$cred = run_credential(['-f', '///nosuchfile///', 'get'],
|
$cred = run_credential(['-f', '///nosuchfile///', 'get'],
|
||||||
{ host => 'github.com' });
|
{ host => 'github.com' });
|
||||||
|
|
||||||
ok(scalar keys %$cred, 0, "Got 0 keys from missing file");
|
ok(scalar keys %$cred == 0, "Got 0 keys from missing file");
|
||||||
|
|
||||||
chmod 0600, $netrc;
|
chmod 0600, $netrc;
|
||||||
|
|
||||||
print "# Testing with invalid data\n";
|
diag "Testing with invalid data\n";
|
||||||
$cred = run_credential(['-f', $netrc, 'get'],
|
$cred = run_credential(['-f', $netrc, 'get'],
|
||||||
"bad data");
|
"bad data");
|
||||||
ok(scalar keys %$cred, 4, "Got first found keys with bad data");
|
ok(scalar keys %$cred == 4, "Got first found keys with bad data");
|
||||||
|
|
||||||
print "# Testing netrc file for a missing corovamilkbar entry\n";
|
diag "Testing netrc file for a missing corovamilkbar entry\n";
|
||||||
$cred = run_credential(['-f', $netrc, 'get'],
|
$cred = run_credential(['-f', $netrc, 'get'],
|
||||||
{ host => 'corovamilkbar' });
|
{ host => 'corovamilkbar' });
|
||||||
|
|
||||||
ok(scalar keys %$cred, 0, "Got no corovamilkbar keys");
|
ok(scalar keys %$cred == 0, "Got no corovamilkbar keys");
|
||||||
|
|
||||||
print "# Testing netrc file for a github.com entry\n";
|
diag "Testing netrc file for a github.com entry\n";
|
||||||
$cred = run_credential(['-f', $netrc, 'get'],
|
$cred = run_credential(['-f', $netrc, 'get'],
|
||||||
{ host => 'github.com' });
|
{ host => 'github.com' });
|
||||||
|
|
||||||
ok(scalar keys %$cred, 2, "Got 2 Github keys");
|
ok(scalar keys %$cred == 2, "Got 2 Github keys");
|
||||||
|
|
||||||
ok($cred->{password}, 'carolknows', "Got correct Github password");
|
is($cred->{password}, 'carolknows', "Got correct Github password");
|
||||||
ok($cred->{username}, 'carol', "Got correct Github username");
|
is($cred->{username}, 'carol', "Got correct Github username");
|
||||||
|
|
||||||
print "# Testing netrc file for a username-specific entry\n";
|
diag "Testing netrc file for a username-specific entry\n";
|
||||||
$cred = run_credential(['-f', $netrc, 'get'],
|
$cred = run_credential(['-f', $netrc, 'get'],
|
||||||
{ host => 'imap', username => 'bob' });
|
{ host => 'imap', username => 'bob' });
|
||||||
|
|
||||||
ok(scalar keys %$cred, 2, "Got 2 username-specific keys");
|
ok(scalar keys %$cred == 2, "Got 2 username-specific keys");
|
||||||
|
|
||||||
ok($cred->{password}, 'bobwillknow', "Got correct user-specific password");
|
is($cred->{password}, 'bobwillknow', "Got correct user-specific password");
|
||||||
ok($cred->{protocol}, 'imaps', "Got correct user-specific protocol");
|
is($cred->{protocol}, 'imaps', "Got correct user-specific protocol");
|
||||||
|
|
||||||
print "# Testing netrc file for a host:port-specific entry\n";
|
diag "Testing netrc file for a host:port-specific entry\n";
|
||||||
$cred = run_credential(['-f', $netrc, 'get'],
|
$cred = run_credential(['-f', $netrc, 'get'],
|
||||||
{ host => 'imap2:1099' });
|
{ host => 'imap2:1099' });
|
||||||
|
|
||||||
ok(scalar keys %$cred, 2, "Got 2 host:port-specific keys");
|
ok(scalar keys %$cred == 2, "Got 2 host:port-specific keys");
|
||||||
|
|
||||||
ok($cred->{password}, 'tzzknow', "Got correct host:port-specific password");
|
is($cred->{password}, 'tzzknow', "Got correct host:port-specific password");
|
||||||
ok($cred->{username}, 'tzz', "Got correct host:port-specific username");
|
is($cred->{username}, 'tzz', "Got correct host:port-specific username");
|
||||||
|
|
||||||
print "# Testing netrc file that 'host:port kills host' entry\n";
|
diag "Testing netrc file that 'host:port kills host' entry\n";
|
||||||
$cred = run_credential(['-f', $netrc, 'get'],
|
$cred = run_credential(['-f', $netrc, 'get'],
|
||||||
{ host => 'imap2' });
|
{ host => 'imap2' });
|
||||||
|
|
||||||
ok(scalar keys %$cred, 2, "Got 2 'host:port kills host' keys");
|
ok(scalar keys %$cred == 2, "Got 2 'host:port kills host' keys");
|
||||||
|
|
||||||
ok($cred->{password}, 'bobwillknow', "Got correct 'host:port kills host' password");
|
is($cred->{password}, 'bobwillknow', "Got correct 'host:port kills host' password");
|
||||||
ok($cred->{username}, 'bob', "Got correct 'host:port kills host' username");
|
is($cred->{username}, 'bob', "Got correct 'host:port kills host' username");
|
||||||
|
|
||||||
|
diag 'Testing netrc file decryption by git config gpg.program setting\n';
|
||||||
|
$cred = run_credential( ['-f', $netrcGpg, 'get']
|
||||||
|
, { host => 'git-config-gpg' }
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(scalar keys %$cred == 2, 'Got keys decrypted by git config option');
|
||||||
|
|
||||||
|
diag 'Testing netrc file decryption by gpg option\n';
|
||||||
|
$cred = run_credential( ['-f', $netrcGpg, '-g', 'test.command-option-gpg', 'get']
|
||||||
|
, { host => 'command-option-gpg' }
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(scalar keys %$cred == 2, 'Got keys decrypted by command option');
|
||||||
|
|
||||||
sub run_credential
|
sub run_credential
|
||||||
{
|
{
|
||||||
my $args = shift @_;
|
my $args = shift @_;
|
||||||
my $data = shift @_;
|
my $data = shift @_;
|
||||||
my $pid = open2(my $chld_out, my $chld_in,
|
my $pid = open2(my $chld_out, my $chld_in,
|
||||||
'./git-credential-netrc', @global_credential_args,
|
$gcNetrc, @global_credential_args,
|
||||||
@$args);
|
@$args);
|
||||||
|
|
||||||
die "Couldn't open pipe to netrc credential helper: $!" unless $pid;
|
die "Couldn't open pipe to netrc credential helper: $!" unless $pid;
|
||||||
|
Loading…
Reference in New Issue
Block a user