Improvements for clan secrets key generate.
I am not sure to understand what `extract_public_key` was for. It seems like `age-keygen -y` will just work fine for a file like `extract_public_key` is looking for. Unless someone intentionally made a file with a comment like that without the private key in it. Messages are moved to stdout rather being logged. It feels like the output is meaningful in the first step users are going to take. Also makes testing easier, as log messages are captured differently than stdout. The call to add an user is changed to be easier to copy paste and work whether PGP or age is in use. A description for the command is added instead of help which does not seem to be displayed.
This commit is contained in:
@@ -2,57 +2,40 @@ import argparse
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from clan_cli.errors import ClanError
|
from clan_cli.errors import ClanError
|
||||||
from clan_cli.git import commit_files
|
from clan_cli.git import commit_files
|
||||||
|
|
||||||
from . import sops
|
from . import sops
|
||||||
from .secrets import update_secrets
|
from .secrets import update_secrets
|
||||||
from .sops import default_admin_key_path, generate_private_key
|
from .sops import (
|
||||||
|
default_admin_key_path,
|
||||||
|
generate_private_key,
|
||||||
|
maybe_get_admin_public_key,
|
||||||
|
)
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def extract_public_key(filepath: Path) -> str:
|
def generate_key() -> sops.SopsKey:
|
||||||
"""
|
key = maybe_get_admin_public_key()
|
||||||
Extracts the public key from a given text file.
|
if key is not None:
|
||||||
"""
|
print(f"{key.key_type.name} key {key.pubkey} is already set")
|
||||||
try:
|
return key
|
||||||
with filepath.open() as file:
|
|
||||||
for line in file:
|
|
||||||
# Check if the line contains the public key
|
|
||||||
if line.startswith("# public key:"):
|
|
||||||
# Extract and return the public key part after the prefix
|
|
||||||
return line.strip().split(": ")[1]
|
|
||||||
except FileNotFoundError as e:
|
|
||||||
msg = f"The file at {filepath} was not found."
|
|
||||||
raise ClanError(msg) from e
|
|
||||||
except OSError as e:
|
|
||||||
msg = f"An error occurred while extracting the public key: {e}"
|
|
||||||
raise ClanError(msg) from e
|
|
||||||
|
|
||||||
msg = f"Could not find the public key in the file at {filepath}."
|
|
||||||
raise ClanError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_key() -> str:
|
|
||||||
path = default_admin_key_path()
|
path = default_admin_key_path()
|
||||||
if path.exists():
|
_, pub_key = generate_private_key(out_file=path)
|
||||||
log.info(f"Key already exists at {path}")
|
print(
|
||||||
return extract_public_key(path)
|
f"Generated age private key at '{path}' for your user. Please back it up on a secure location or you will lose access to your secrets."
|
||||||
priv_key, pub_key = generate_private_key(out_file=path)
|
|
||||||
log.info(
|
|
||||||
f"Generated age private key at '{default_admin_key_path()}' for your user. Please back it up on a secure location or you will lose access to your secrets."
|
|
||||||
)
|
)
|
||||||
return pub_key
|
return sops.SopsKey(pub_key, username="", key_type=sops.KeyType.AGE)
|
||||||
|
|
||||||
|
|
||||||
def generate_command(args: argparse.Namespace) -> None:
|
def generate_command(args: argparse.Namespace) -> None:
|
||||||
pub_key = generate_key()
|
key = generate_key()
|
||||||
log.info(
|
print("Also add your age public key to the repository with:")
|
||||||
f"Also add your age public key to the repository with: \nclan secrets users add <username> {pub_key}"
|
key_type = key.key_type.name.lower()
|
||||||
)
|
print(f"clan secrets users add --{key_type}-key <username>")
|
||||||
|
|
||||||
|
|
||||||
def show_command(args: argparse.Namespace) -> None:
|
def show_command(args: argparse.Namespace) -> None:
|
||||||
@@ -76,7 +59,13 @@ def register_key_parser(parser: argparse.ArgumentParser) -> None:
|
|||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser_generate = subparser.add_parser("generate", help="generate age key")
|
parser_generate = subparser.add_parser(
|
||||||
|
"generate",
|
||||||
|
description=(
|
||||||
|
"Generate an age key for the Clan, "
|
||||||
|
"to use PGP set `SOPS_PGP_FP` in your environment."
|
||||||
|
),
|
||||||
|
)
|
||||||
parser_generate.set_defaults(func=generate_command)
|
parser_generate.set_defaults(func=generate_command)
|
||||||
|
|
||||||
parser_show = subparser.add_parser("show", help="show public key")
|
parser_show = subparser.add_parser("show", help="show public key")
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ def register_users_parser(parser: argparse.ArgumentParser) -> None:
|
|||||||
"--pgp-key",
|
"--pgp-key",
|
||||||
help=(
|
help=(
|
||||||
"public PGP encryption key of the user. "
|
"public PGP encryption key of the user. "
|
||||||
|
# Use --fingerprint --fingerprint to get fingerprints for subkeys:
|
||||||
"Execute `gpg -k --fingerprint --fingerprint` and remove spaces to get it."
|
"Execute `gpg -k --fingerprint --fingerprint` and remove spaces to get it."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user