clan-cli: rebase sops changes on top of vars changes

vars changes in question are from commit: 54b8f5904e

With this changeset the age specific sops logic that was added is now
generic.

To keep things simple, this changeset modifies `SopsKey` so that
`username` is ignored when comparing different keys. I don't really see
us relying on `username` and this makes `SopsKey` hashable, and usable
in a `set`, which is nice when you check that you have a particular key.
This commit is contained in:
Louis Opter
2024-11-18 17:11:25 -08:00
committed by Mic92
parent 8d53568d95
commit 1ba27196d8
2 changed files with 61 additions and 31 deletions

View File

@@ -1,3 +1,4 @@
import dataclasses
import enum
import functools
import io
@@ -8,7 +9,6 @@ import shutil
import subprocess
from collections.abc import Iterable, Sequence
from contextlib import suppress
from dataclasses import dataclass
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import IO, Any, Protocol
@@ -34,11 +34,26 @@ class KeyType(enum.Enum):
return cls.__members__.get(value.upper())
return None
@property
def sops_recipient_attr(self) -> str:
"""Name of the attribute to get the recipient key from a Sops file."""
if self == self.AGE:
return "recipient"
if self == self.PGP:
return "fp"
msg = (
f"KeyType is not properly implemented: "
f'"sops_recipient_attr" is missing for key type "{self.name}"'
)
raise ClanError(msg)
@dataclass(frozen=True, eq=False)
@dataclasses.dataclass(frozen=True)
class SopsKey:
pubkey: str
username: str
# Two SopsKey are considered equal even
# if they don't have the same username:
username: str = dataclasses.field(compare=False)
key_type: KeyType
def as_dict(self) -> dict[str, str]:
@@ -48,6 +63,13 @@ class SopsKey:
"type": self.key_type.name.lower(),
}
@classmethod
def load_dir(cls, dir: Path) -> "SopsKey": # noqa: ANN102
"""Load from the file named `keys.json` in the given directory."""
pubkey, key_type = read_key(dir)
username = ""
return cls(pubkey, username, key_type)
class ExitStatus(enum.IntEnum): # see: cmd/sops/codes/codes.go
ERROR_GENERIC = 1
@@ -389,6 +411,19 @@ def decrypt_file(secret_path: Path) -> str:
return stdout
def get_recipients(secret_path: Path) -> set[SopsKey]:
sops_attrs = json.loads((secret_path / "secret").read_text())["sops"]
return {
SopsKey(
pubkey=recipient[key_type.sops_recipient_attr],
username="",
key_type=key_type,
)
for key_type in KeyType
for recipient in sops_attrs[key_type.name.lower()]
}
def get_meta(secret_path: Path) -> dict:
meta_path = secret_path.parent / "meta.json"
if not meta_path.exists():