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 export
from .keyring import inspect_keyring from .keyring import inspect_keyring
from .keyring import list_keyring from .keyring import list_keyring
from .types import TrustFilter
from .util import absolute_path from .util import absolute_path
from .util import cwd from .util import cwd
from .verify import verify from .verify import verify
@ -81,6 +82,12 @@ list_parser = subcommands.add_parser(
help="list the certificates in the keyring", 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("--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( list_parser.add_argument(
"source", "source",
nargs="*", nargs="*",
@ -180,10 +187,12 @@ def main() -> None: # noqa: ignore=C901
target_dir=keyring_root.parent / "build", target_dir=keyring_root.parent / "build",
) )
elif "list" == args.subcommand: elif "list" == args.subcommand:
trust_filter = TrustFilter[args.trust]
list_keyring( list_keyring(
keyring_root=keyring_root, keyring_root=keyring_root,
sources=args.source, sources=args.source,
main_keys=args.main, main_keys=args.main,
trust_filter=trust_filter,
) )
elif "inspect" == args.subcommand: elif "inspect" == args.subcommand:
print( print(

View File

@ -25,9 +25,11 @@ from .sequoia import packet_signature_creation_time
from .sequoia import packet_split from .sequoia import packet_split
from .trust import certificate_trust from .trust import certificate_trust
from .trust import certificate_trust_from_paths from .trust import certificate_trust_from_paths
from .trust import filter_by_trust
from .trust import format_trust_label from .trust import format_trust_label
from .types import Fingerprint from .types import Fingerprint
from .types import Trust from .types import Trust
from .types import TrustFilter
from .types import Uid from .types import Uid
from .types import Username from .types import Username
from .util import contains_fingerprint 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 """List certificates in the keyring
If sources contains directories, all certificate below them are considered. 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 sources: A list of username, fingerprint or directories from which to read PGP packet information
(defaults to `keyring_root`) (defaults to `keyring_root`)
main_keys: List main keys instead of packager keys (defaults to False) 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") 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"]), main_keys=get_fingerprints_from_paths([keyring_root / "main"]),
all_fingerprints=get_fingerprints_from_paths([keyring_root]), 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) trust_label = format_trust_label(trust=trust)
print(f"{username:<{username_length}} {certificate.name} {trust_label}") 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 Color
from .types import Fingerprint from .types import Fingerprint
from .types import Trust from .types import Trust
from .types import TrustFilter
from .types import Uid from .types import Uid
from .util import contains_fingerprint from .util import contains_fingerprint
from .util import get_cert_paths 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 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}" 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() 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]) 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) 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] 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] 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") @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 libkeyringctl.trust import certificate_trust_from_paths 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 format_trust_label
from libkeyringctl.trust import trust_color from libkeyringctl.trust import trust_color
from libkeyringctl.trust import trust_icon from libkeyringctl.trust import trust_icon
from libkeyringctl.types import Color from libkeyringctl.types import Color
from libkeyringctl.types import Fingerprint from libkeyringctl.types import Fingerprint
from libkeyringctl.types import Trust from libkeyringctl.types import Trust
from libkeyringctl.types import TrustFilter
from libkeyringctl.types import Uid from libkeyringctl.types import Uid
from libkeyringctl.types import Username 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_icon_mock.return_value = "ICON"
trust_color_mock.return_value = Color.GREEN trust_color_mock.return_value = Color.GREEN
assert f"{Color.GREEN.value}ICON {trust.name}{Color.RST.value}" == format_trust_label(trust) 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