Skip to content
Permalink
Browse files

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
  • Loading branch information...
gitster committed May 30, 2018
2 parents ab48bc0 + 786ef50 commit 017b7c52fc00897c72f6c4808ded43c4c5f9c5b8
@@ -1,5 +1,5 @@
test:
./test.pl
./t-git-credential-netrc.sh

testverbose:
./test.pl -d -v
./t-git-credential-netrc.sh -d -v
@@ -2,11 +2,13 @@

use strict;
use warnings;
use autodie;

use Getopt::Long;
use File::Basename;
use Git;

my $VERSION = "0.1";
my $VERSION = "0.2";

my %options = (
help => 0,
@@ -54,6 +56,7 @@ GetOptions(\%options,
"insecure|k",
"verbose|v",
"file|f=s@",
'gpg|g:s',
);

if ($options{help}) {
@@ -62,27 +65,31 @@ if ($options{help}) {

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.
Options:
-f|--file AUTHFILE : specify netrc-style files. Files with the .gpg extension
will be decrypted by GPG before parsing. Multiple -f
arguments are OK. They are processed in order, and the
first matching entry found is returned via the credential
helper protocol (see below).
-f|--file <authfile>: specify netrc-style files. Files with the .gpg
extension will be decrypted by GPG before parsing.
Multiple -f arguments are OK. They are processed in
order, and the first matching entry found is returned
via the credential helper protocol (see below).
When no -f option is given, .authinfo.gpg, .netrc.gpg,
.authinfo, and .netrc files in your home directory are used
in this order.
When no -f option is given, .authinfo.gpg, .netrc.gpg,
.authinfo, and .netrc files in your home directory are
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:
@@ -99,8 +106,9 @@ in the path.)
git config credential.helper '$shortname -f AUTHFILE -v'
Only "get" mode is supported by this credential helper. It opens every AUTHFILE
and looks for the first entry that matches the requested search criteria:
Only "get" mode is supported by this credential helper. It opens every
<authfile> and looks for the first entry that matches the requested search
criteria:
'port|protocol':
The protocol that will be used (e.g., https). (protocol=X)
@@ -120,7 +128,7 @@ host=github.com
protocol=https
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
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
skipped.
Again, note that only the first matching entry from all the AUTHFILEs, processed
in the sequence given on the command line, is used.
Again, note that only the first matching entry from all the <authfile>s,
processed in the sequence given on the command line, is used.
Netrc/authinfo tokens can be quoted as 'STRING' or "STRING".
@@ -152,7 +160,7 @@ EOHIPPUS
my $mode = shift @ARGV;

# 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.
exit 0 unless $mode eq 'get';
@@ -172,6 +180,8 @@ unless (scalar @$files) {
$files = $options{file} = [ map { glob $_ } @candidates ];
}

load_config(\%options);

my $query = read_credential_data_from_stdin();

FILE:
@@ -233,7 +243,7 @@ sub load_netrc {

my $io;
if ($gpgmode) {
my @cmd = (qw(gpg --decrypt), $file);
my @cmd = ($options{'gpg'}, qw(--decrypt), $file);
log_verbose("Using GPG to open $file: [@cmd]");
open $io, "-|", @cmd;
} else {
@@ -410,6 +420,14 @@ sub print_credential_data {
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 {
return unless $options{verbose};
printf STDERR @_;
@@ -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
)
@@ -0,0 +1,2 @@
#!/bin/sh
echo machine command-option-gpg login username password password
@@ -0,0 +1,2 @@
#!/bin/sh
echo machine git-config-gpg login username password password
No changes.
@@ -1,83 +1,115 @@
#!/usr/bin/perl
use lib (split(/:/, $ENV{GITPERLLIB}));

use warnings;
use strict;
use Test;
use Test::More qw(no_plan);
use File::Basename;
use File::Spec::Functions qw(:DEFAULT rel2abs);
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 $netrc = './test.netrc';
print "# Testing insecure file, nothing should be found\n";
my $scriptDir = dirname rel2abs $0;
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;
my $cred = run_credential(['-f', $netrc, 'get'],
{ 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;
$cred = run_credential(['-f', '///nosuchfile///', 'get'],
{ 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;

print "# Testing with invalid data\n";
diag "Testing with invalid data\n";
$cred = run_credential(['-f', $netrc, 'get'],
"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'],
{ 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'],
{ 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");
ok($cred->{username}, 'carol', "Got correct Github username");
is($cred->{password}, 'carolknows', "Got correct Github password");
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'],
{ 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");
ok($cred->{protocol}, 'imaps', "Got correct user-specific protocol");
is($cred->{password}, 'bobwillknow', "Got correct user-specific password");
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'],
{ 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");
ok($cred->{username}, 'tzz', "Got correct host:port-specific username");
is($cred->{password}, 'tzzknow', "Got correct host:port-specific password");
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'],
{ 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");

is($cred->{password}, 'bobwillknow', "Got correct 'host:port kills host' password");
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($cred->{password}, 'bobwillknow', "Got correct 'host:port kills host' password");
ok($cred->{username}, 'bob', "Got correct 'host:port kills host' username");
ok(scalar keys %$cred == 2, 'Got keys decrypted by command option');

sub run_credential
{
my $args = shift @_;
my $data = shift @_;
my $pid = open2(my $chld_out, my $chld_in,
'./git-credential-netrc', @global_credential_args,
$gcNetrc, @global_credential_args,
@$args);

die "Couldn't open pipe to netrc credential helper: $!" unless $pid;

0 comments on commit 017b7c5

Please sign in to comment.
You can’t perform that action at this time.