feature(keyringctl): support passing fingerprint as source

This helps make the CLI more useful by listing, exporting or inspecting
a specific fingerprint.
This commit is contained in:
Levente Polyak 2021-10-23 03:01:06 +02:00
parent 6d336828e1
commit 5249453726
No known key found for this signature in database
GPG Key ID: FC1B547C8D8172C8
2 changed files with 55 additions and 23 deletions

View File

@ -56,14 +56,9 @@ Export the whole keyring including main and packager to stdout
./keyringctl export
```
Limit to specific usernames using an output file
Limit to specific certs using an output file
```bash
./keyringctl export <usernames...> --output <filename>
```
Only export specific certificate directories in [keyring](keyring)
```bash
./keyringctl export <directory...>
./keyringctl export <username_or_fingerprint_or_directory...> --output <filename>
```
### List
@ -75,7 +70,7 @@ List all certificates in the keyring
Only show a specific main key
```bash
./keyringctl list --main <usernames...>
./keyringctl list --main <username_or_fingerprint...>
```
### Inspect
@ -87,12 +82,7 @@ Inspect all certificates in the keyring
Only inspect a specific main key
```bash
./keyringctl inspect --main <usernames...>
```
Specify directories to inspect a single fingerprint
```bash
./keyringctl inspect <directories...>
./keyringctl inspect --main <username_or_fingerprint_or_directory...>
```
## Installation

View File

@ -16,6 +16,7 @@ from os import chdir
from os import getcwd
from pathlib import Path
from re import escape
from re import match
from re import split
from re import sub
from shutil import copytree
@ -128,6 +129,22 @@ def system(cmd: List[str], exit_on_error: bool = False) -> str:
raise e
def is_pgp_fingerprint(string: str) -> bool:
"""Returns whether the passed string looks like a PGP (long) fingerprint
Parameters
----------
string: Input to consider as a fingerprint
Returns
-------
RWhether string is a fingerprint
"""
if len(string) not in [16, 40]:
return False
return match("^[A-F0-9]+$", string) is not None
def get_cert_paths(paths: Iterable[Path]) -> Set[Path]:
"""Walks a list of paths and resolves all discovered certificate paths
@ -1018,7 +1035,8 @@ def export(
----------
working_dir: A directory to use for temporary files
keyring_root: The keyring root directory to look up username shorthand sources
sources: A list of directories or files from which to read PGP packet information (defaults to `keyring_root`)
sources: A list of username, fingerprint or directories from which to read PGP packet information
(defaults to `keyring_root`)
output: An output file that all PGP packet data is written to, return the result instead if None
Returns
@ -1031,9 +1049,17 @@ def export(
# resolve shorthand username exports for packager keys
for index, source in enumerate(sources):
if source.exists():
continue
packager_source = keyring_root / "packager" / source.name
if not source.exists() and packager_source.exists():
if packager_source.exists():
sources[index] = packager_source
continue
if is_pgp_fingerprint(source.name):
fingerprint_paths = list(keyring_root.glob(f"*/*/*{source.name}"))
if fingerprint_paths:
sources[index] = fingerprint_paths[0]
continue
temp_dir = Path(mkdtemp(dir=working_dir, prefix="arch-keyringctl-export-join-")).absolute()
cert_paths: Set[Path] = get_cert_paths(sources)
@ -1090,7 +1116,7 @@ def list_keyring(keyring_root: Path, sources: Optional[List[Path]] = None, main_
Parameters
----------
keyring_root: Path The keyring root directory to look up username shorthand sources
sources: A list of username or files/directories from which to read PGP packet information
sources: A list of username, fingerprint or directories from which to read PGP packet information
(defaults to `keyring_root`)
main_keys: List main keys instead of packager keys (defaults to False)
"""
@ -1102,9 +1128,17 @@ def list_keyring(keyring_root: Path, sources: Optional[List[Path]] = None, main_
# resolve shorthand username exports for packager keys
for index, source in enumerate(sources):
if source.exists():
continue
packager_source = keyring_dir / source.name
if not source.exists() and packager_source.exists():
if packager_source.exists():
sources[index] = packager_source
continue
if is_pgp_fingerprint(source.name):
fingerprint_paths = list(keyring_root.glob(f"*/*/*{source.name}"))
if fingerprint_paths:
sources[index] = fingerprint_paths[0].parent
continue
username_length = max([len(source.name) for source in sources])
@ -1122,7 +1156,7 @@ def inspect_keyring(working_dir: Path, keyring_root: Path, sources: Optional[Lis
----------
working_dir: A directory to use for temporary files
keyring_root: The keyring root directory to look up username shorthand sources
sources: A list of username or files/directories from which to read PGP packet information
sources: A list of username, fingerprint or directories from which to read PGP packet information
(defaults to `keyring_root`)
Returns
@ -1135,9 +1169,17 @@ def inspect_keyring(working_dir: Path, keyring_root: Path, sources: Optional[Lis
# resolve shorthand username exports for packager keys
for index, source in enumerate(sources):
if source.exists():
continue
packager_source = keyring_root / "packager" / source.name
if not source.exists() and packager_source.exists():
if packager_source.exists():
sources[index] = packager_source
continue
if is_pgp_fingerprint(source.name):
fingerprint_paths = list(keyring_root.glob(f"*/*/*{source.name}"))
if fingerprint_paths:
sources[index] = fingerprint_paths[0]
continue
keyring = Path(mkstemp(dir=working_dir, prefix="packet-", suffix=".asc")[1]).absolute()
export(working_dir=working_dir, keyring_root=keyring_root, sources=sources, output=keyring)
@ -1213,7 +1255,7 @@ if __name__ == "__main__":
export_parser.add_argument(
"source",
nargs="*",
help="username or files/directories containing PGP packet data (can be provided multiple times)",
help="username, fingerprint or directories containing certificates",
type=absolute_path,
)
@ -1230,7 +1272,7 @@ if __name__ == "__main__":
list_parser.add_argument(
"source",
nargs="*",
help="username or files/directories containing certificates (can be provided multiple times)",
help="username, fingerprint or directories containing certificates",
type=absolute_path,
)
@ -1241,7 +1283,7 @@ if __name__ == "__main__":
inspect_parser.add_argument(
"source",
nargs="*",
help="username or directories containing certificates",
help="username, fingerprint or directories containing certificates",
type=absolute_path,
)