Skip to content

Commit 5de8d71

Browse files
Allow gnupg key fingerprints for tell and removeperson (#1216)
* Allow GPG keys to be specified by fingerprint in addition to email Add support for identifying GPG keys by fingerprint (8+ hex characters) in the 'tell' and 'removeperson' commands. Previously, only email addresses were accepted. Changes: - Add _is_gpg_fingerprint() to detect fingerprint vs email input - Add _check_fingerprint_in_keyring() to verify fingerprint in keyring - Update _assert_keyring_emails() to handle fingerprint validation - Update error messages and documentation to mention fingerprints - Add tests for tell/removeperson with fingerprints
1 parent 248bf3f commit 5de8d71

File tree

8 files changed

+135
-25
lines changed

8 files changed

+135
-25
lines changed

man/man1/git-secret-removeperson.1.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ git-secret-removeperson - removes user's public key from repo keyring.
33

44
## SYNOPSIS
55

6-
git secret removeperson <emails>...
6+
git secret removeperson <emails or fingerprints>...
77

88

99
## DESCRIPTION
10-
`git-secret-removeperson` - removes public keys for passed email addresses from repo's `git-secret` keyring.
10+
`git-secret-removeperson` - removes public keys for passed email addresses or GPG fingerprints from repo's `git-secret` keyring.
1111

1212
This command is used to begin the process of disallowing a user from encrypting and decrypting secrets with `git-secret`.
1313

man/man1/git-secret-tell.1.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ git-secret-tell - adds person who can access private data.
33

44
## SYNOPSIS
55

6-
git secret tell [-m] [-d dir] [emails]...
6+
git secret tell [-m] [-d dir] [emails or fingerprints]...
77

88

99
## DESCRIPTION
@@ -14,12 +14,14 @@ but will not immediately be able to decrypt existing files, which were encrypted
1414
Files should be re-encrypted with the new keyring by someone who already has access
1515
in order for the new user to be able to decrypt the files.
1616

17-
`git-secret tell` works only with email addresses, and will exit with an error if you have
17+
`git-secret tell` works with email addresses or GPG key fingerprints, and will exit with an error if you have
1818
multiple keys in your keyring with specified email addresses, or if one of the specified emails
1919
is already associated with a key in the `git-secret` repo's keyring.
2020

2121
Under the hood, `git-secret-tell` searches in the current user's `gnupg` keyring for public key(s) of passed
22-
email(s), then imports the corresponding public key(s) into your `git-secret` repo's keyring.
22+
email(s) or fingerprint(s), then imports the corresponding public key(s) into your `git-secret` repo's keyring.
23+
24+
Fingerprints should be specified as a hex string of 8 or more characters (e.g., full 40-character fingerprints).
2325

2426
Versions of `git-secret tell` after `0.3.2` will warn about keys that are expired, revoked, or otherwise invalid.
2527
It will also warn if multiple keys are found for a single email address.

src/_utils/_git_secret_tools.sh

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,37 @@ function _assert_keyring_contains_emails_at_least_once {
638638

639639

640640

641+
function _is_gpg_fingerprint {
642+
# Returns 0 if the input looks like a GPG fingerprint or key ID
643+
# (8+ hex characters), 1 otherwise.
644+
local input="$1"
645+
local clean_input
646+
clean_input=$(echo "$input" | tr -d ' ')
647+
if [[ "$clean_input" =~ ^[A-Fa-f0-9]{8,}$ ]]; then
648+
return 0
649+
else
650+
return 1
651+
fi
652+
}
653+
654+
655+
function _check_fingerprint_in_keyring {
656+
# Checks if a key matching the given fingerprint exists in the keyring.
657+
# Returns 0 if found, 1 if not found.
658+
local homedir="$1"
659+
local fingerprint="$2"
660+
661+
local args=()
662+
if [[ -n "$homedir" ]]; then
663+
args+=( "--homedir" "$homedir" )
664+
fi
665+
666+
# 3>&- closes fd 3 for bats
667+
$SECRETS_GPG_COMMAND "${args[@]}" --no-permission-warning --list-keys "$fingerprint" > /dev/null 2>&1 3>&-
668+
return $?
669+
}
670+
671+
641672
function _assert_keyring_emails {
642673
local homedir="$1"
643674
local keyring_name="$2"
@@ -651,27 +682,40 @@ function _assert_keyring_emails {
651682
local gpg_uids
652683
gpg_uids=$(_get_users_in_gpg_keyring "$homedir")
653684
for email in "${emails[@]}"; do
654-
if [[ $email != *"@"* ]]; then
655-
_abort "does not appear to be an email: $email"
656-
fi
657-
local emails_found=0
658-
for uid in $gpg_uids; do
659-
if [[ "$uid" == "$email" ]]; then
660-
emails_found=$((emails_found+1))
661-
fi
662-
done
663-
if [[ $expected -eq 1 ]]; then
664-
if [[ $emails_found -eq 0 ]]; then
665-
_abort "no key found in gpg $keyring_name for: $email"
666-
elif [[ $emails_found -gt 1 ]]; then
667-
if [[ $allow_duplicates -ne 1 ]]; then
668-
_abort "$emails_found keys found in gpg $keyring_name for: $email"
669-
fi
685+
if _is_gpg_fingerprint "$email"; then
686+
# For fingerprints, check directly with gpg
687+
if [[ $expected -eq 1 ]]; then
688+
if ! _check_fingerprint_in_keyring "$homedir" "$email"; then
689+
_abort "no key found in gpg $keyring_name for fingerprint: $email"
690+
fi
691+
else
692+
if _check_fingerprint_in_keyring "$homedir" "$email"; then
693+
_abort "key already exists in gpg $keyring_name for fingerprint: $email"
670694
fi
695+
fi
671696
else
672-
if [[ $emails_found -gt 0 ]]; then
673-
_abort "$emails_found keys found in gpg $keyring_name for: $email"
697+
if [[ $email != *"@"* ]]; then
698+
_abort "does not appear to be an email or fingerprint: $email"
699+
fi
700+
local emails_found=0
701+
for uid in $gpg_uids; do
702+
if [[ "$uid" == "$email" ]]; then
703+
emails_found=$((emails_found+1))
674704
fi
705+
done
706+
if [[ $expected -eq 1 ]]; then
707+
if [[ $emails_found -eq 0 ]]; then
708+
_abort "no key found in gpg $keyring_name for: $email"
709+
elif [[ $emails_found -gt 1 ]]; then
710+
if [[ $allow_duplicates -ne 1 ]]; then
711+
_abort "$emails_found keys found in gpg $keyring_name for: $email"
712+
fi
713+
fi
714+
else
715+
if [[ $emails_found -gt 0 ]]; then
716+
_abort "$emails_found keys found in gpg $keyring_name for: $email"
717+
fi
718+
fi
675719
fi
676720

677721
done

src/commands/git_secret_removeperson.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function removeperson {
2222
local emails=( "$@" )
2323

2424
if [[ ${#emails[@]} -eq 0 ]]; then
25-
_abort "at least one email is required for removeperson."
25+
_abort "at least one email or fingerprint is required for removeperson."
2626
fi
2727
# Getting the local git-secret `gpg` key directory:
2828
local secrets_dir_keys

src/commands/git_secret_tell.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function tell {
6464
if [[ "${#emails[@]}" -eq 0 ]]; then
6565
# If after possible addition of git_email, emails are still empty,
6666
# we should raise an exception.
67-
_abort "you must use -m or provide at least one email address."
67+
_abort "you must use -m or provide at least one email address or fingerprint."
6868
fi
6969

7070
local secrets_dir_keys

tests/_test_base.bash

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,17 @@ function get_gpg_fingerprint_by_email {
148148
}
149149

150150

151+
function get_gpg_public_fingerprint_by_email {
152+
local email="$1"
153+
local fingerprint
154+
155+
fingerprint=$(_gpgtest --with-fingerprint \
156+
--with-colon \
157+
--list-keys "$email" | gawk "$AWK_GPG_GET_FP")
158+
echo "$fingerprint"
159+
}
160+
161+
151162
function install_fixture_key {
152163
local public_key="$BATS_TMPDIR/public-${1}.key"
153164

tests/test_removeperson.bats

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,19 @@ function teardown {
111111
[ "$status" -eq 1 ]
112112
}
113113

114+
115+
@test "run 'removeperson' with fingerprint" {
116+
local fingerprint
117+
fingerprint=$(get_gpg_public_fingerprint_by_email "$TEST_DEFAULT_USER")
118+
119+
run git secret removeperson "$fingerprint"
120+
[ "$status" -eq 0 ]
121+
122+
# Testing output:
123+
[[ "$output" == *"removed keys"* ]]
124+
125+
# Then whoknows must return an error with status code 1:
126+
run git secret whoknows
127+
[ "$status" -eq 1 ]
128+
}
129+

tests/test_tell.bats

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,40 @@ function teardown {
249249
cd "$current_dir"
250250
rm -r "$root_dir"
251251
}
252+
253+
254+
@test "run 'tell' with fingerprint" {
255+
local fingerprint
256+
fingerprint=$(get_gpg_public_fingerprint_by_email "$TEST_DEFAULT_USER")
257+
258+
run git secret tell -d "$TEST_GPG_HOMEDIR" "$fingerprint"
259+
[ "$status" -eq 0 ]
260+
261+
# Testing that now user is found:
262+
run _user_required
263+
[ "$status" -eq 0 ]
264+
265+
# Testing that now user is in the list of people who knows the secret:
266+
run git secret whoknows
267+
[[ "$output" == *"$TEST_DEFAULT_USER"* ]]
268+
}
269+
270+
271+
@test "run 'tell' with fingerprint on the same key twice" {
272+
local fingerprint
273+
fingerprint=$(get_gpg_public_fingerprint_by_email "$TEST_DEFAULT_USER")
274+
275+
# first time should succeed
276+
git secret tell -d "$TEST_GPG_HOMEDIR" "$fingerprint"
277+
278+
# second time should fail because the key is already in the keyring
279+
run git secret tell -d "$TEST_GPG_HOMEDIR" "$fingerprint"
280+
[ "$status" -ne 0 ]
281+
}
282+
283+
284+
@test "run 'tell' with invalid fingerprint" {
285+
# A hex string that doesn't match any key
286+
run git secret tell -d "$TEST_GPG_HOMEDIR" "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
287+
[ "$status" -eq 1 ]
288+
}

0 commit comments

Comments
 (0)