feature(keyringctl): add option to filter listing by trust

This commit is contained in:
Levente Polyak 2021-11-18 20:14:45 +01:00
parent e9dc04df32
commit c651bdc61e
No known key found for this signature in database
GPG Key ID: FC1B547C8D8172C8
6 changed files with 94 additions and 1 deletions

View File

@ -15,6 +15,7 @@ from .keyring import convert
from .keyring import export
from .keyring import inspect_keyring
from .keyring import list_keyring
from .types import TrustFilter
from .util import absolute_path
from .util import cwd
from .verify import verify
@ -81,6 +82,12 @@ list_parser = subcommands.add_parser(
help="list the certificates in the keyring",
)
list_parser.add_argument("--main", action="store_true", help="List main signing keys instead of packager keys")
list_parser.add_argument(
"--trust",
choices=[e.value for e in TrustFilter],
default=TrustFilter.all.value,
help="Filter the list based on trust",
)
list_parser.add_argument(
"source",
nargs="*",
@ -180,10 +187,12 @@ def main() -> None: # noqa: ignore=C901
target_dir=keyring_root.parent / "build",
)
elif "list" == args.subcommand:
trust_filter = TrustFilter[args.trust]
list_keyring(
keyring_root=keyring_root,
sources=args.source,
main_keys=args.main,
trust_filter=trust_filter,
)
elif "inspect" == args.subcommand:
print(

View File

@ -25,9 +25,11 @@ from .sequoia import packet_signature_creation_time
from .sequoia import packet_split
from .trust import certificate_trust
from .trust import certificate_trust_from_paths
from .trust import filter_by_trust
from .trust import format_trust_label
from .types import Fingerprint
from .types import Trust
from .types import TrustFilter
from .types import Uid
from .types import Username
from .util import contains_fingerprint
@ -1097,7 +1099,12 @@ def build(
)
def list_keyring(keyring_root: Path, sources: Optional[List[Path]] = None, main_keys: bool = False) -> None:
def list_keyring(
keyring_root: Path,
sources: Optional[List[Path]] = None,
main_keys: bool = False,
trust_filter: TrustFilter = TrustFilter.all,
) -> None:
"""List certificates in the keyring
If sources contains directories, all certificate below them are considered.
@ -1108,6 +1115,7 @@ def list_keyring(keyring_root: Path, sources: Optional[List[Path]] = None, main_
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)
trust_filter: Filter the listing based on trust
"""
keyring_dir = keyring_root / ("main" if main_keys else "packager")
@ -1130,6 +1138,8 @@ def list_keyring(keyring_root: Path, sources: Optional[List[Path]] = None, main_
main_keys=get_fingerprints_from_paths([keyring_root / "main"]),
all_fingerprints=get_fingerprints_from_paths([keyring_root]),
)
if not filter_by_trust(trust=trust, trust_filter=trust_filter):
continue
trust_label = format_trust_label(trust=trust)
print(f"{username:<{username_length}} {certificate.name} {trust_label}")

View File

@ -10,6 +10,7 @@ from typing import Set
from .types import Color
from .types import Fingerprint
from .types import Trust
from .types import TrustFilter
from .types import Uid
from .util import contains_fingerprint
from .util import get_cert_paths
@ -236,3 +237,26 @@ def format_trust_label(trust: Trust) -> str:
Text label representing the trust status as literal and icon with colors
"""
return f"{trust_color(trust).value}{trust_icon(trust)} {trust.name}{Color.RST.value}"
def filter_by_trust(trust: Trust, trust_filter: TrustFilter) -> bool:
"""Filters a trust by a given filter and returns true if within the rules
Parameters
----------
trust: Trust to check for being filtered
trust_filter: Filter rules to check the trust against
Returns
-------
True if the given trust is within the filter rules
"""
trust_map = {
TrustFilter.unknown: [Trust.unknown],
TrustFilter.marginal: [Trust.marginal],
TrustFilter.full: [Trust.full],
TrustFilter.revoked: [Trust.revoked],
TrustFilter.unrevoked: [Trust.unknown, Trust.marginal, Trust.full],
TrustFilter.all: [Trust.revoked, Trust.unknown, Trust.marginal, Trust.full],
}
return trust in trust_map[trust_filter]

View File

@ -17,6 +17,15 @@ class Trust(Enum):
full = auto()
class TrustFilter(Enum):
unknown = "unknown"
revoked = "revoked"
marginal = "marginal"
full = "full"
unrevoked = "unrevoked"
all = "all"
TRUST_MAX_LENGTH: int = max([len(e.name) for e in Trust])

View File

@ -728,6 +728,12 @@ def test_list_keyring(working_dir: Path, keyring_dir: Path) -> None:
keyring.list_keyring(keyring_root=keyring_dir, sources=[path], main_keys=False)
print_args = [mock_call[1][0] for mock_call in print_mock.mock_calls]
assert name in print_args[0] and path.stem in print_args[0]
with patch("builtins.print") as print_mock:
keyring.list_keyring(
keyring_root=keyring_dir, sources=paths, main_keys=False, trust_filter=TrustFilter.revoked
)
print_args = [mock_call[1][0] for mock_call in print_mock.mock_calls]
assert not print_args
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")

View File

@ -8,12 +8,14 @@ from pytest import raises
from libkeyringctl.trust import certificate_trust
from libkeyringctl.trust import certificate_trust_from_paths
from libkeyringctl.trust import filter_by_trust
from libkeyringctl.trust import format_trust_label
from libkeyringctl.trust import trust_color
from libkeyringctl.trust import trust_icon
from libkeyringctl.types import Color
from libkeyringctl.types import Fingerprint
from libkeyringctl.types import Trust
from libkeyringctl.types import TrustFilter
from libkeyringctl.types import Uid
from libkeyringctl.types import Username
@ -327,3 +329,36 @@ def test_format_trust_label(trust_color_mock: Mock, trust_icon_mock: Mock, trust
trust_icon_mock.return_value = "ICON"
trust_color_mock.return_value = Color.GREEN
assert f"{Color.GREEN.value}ICON {trust.name}{Color.RST.value}" == format_trust_label(trust)
@mark.parametrize(
"trust, trust_filter, result",
[
(Trust.revoked, TrustFilter.unknown, False),
(Trust.full, TrustFilter.unknown, False),
(Trust.marginal, TrustFilter.unknown, False),
(Trust.unknown, TrustFilter.unknown, True),
(Trust.revoked, TrustFilter.marginal, False),
(Trust.full, TrustFilter.marginal, False),
(Trust.marginal, TrustFilter.marginal, True),
(Trust.unknown, TrustFilter.marginal, False),
(Trust.revoked, TrustFilter.full, False),
(Trust.full, TrustFilter.full, True),
(Trust.marginal, TrustFilter.full, False),
(Trust.unknown, TrustFilter.full, False),
(Trust.revoked, TrustFilter.revoked, True),
(Trust.full, TrustFilter.revoked, False),
(Trust.marginal, TrustFilter.revoked, False),
(Trust.unknown, TrustFilter.revoked, False),
(Trust.revoked, TrustFilter.unrevoked, False),
(Trust.full, TrustFilter.unrevoked, True),
(Trust.marginal, TrustFilter.unrevoked, True),
(Trust.unknown, TrustFilter.unrevoked, True),
(Trust.revoked, TrustFilter.all, True),
(Trust.full, TrustFilter.all, True),
(Trust.marginal, TrustFilter.all, True),
(Trust.unknown, TrustFilter.all, True),
],
)
def test_filter_by_trust(trust: Trust, trust_filter: TrustFilter, result: bool) -> None:
assert filter_by_trust(trust=trust, trust_filter=trust_filter) == result