fix TUI crash on serial consoles; make index.txt the sole email store
curses.use_default_colors() raises on terminals (e.g. serial consoles using TERM=linux/vt100) whose terminfo lacks default-color support; init_colors() now falls back to an explicit black background and caps the "disabled" color to 8-color terminals. --reissue without --email silently sent no mail when the CN wasn't in openvpncertupdate-metadata.json, even though its email was already embedded in the cert's index.txt subject (via EASYRSA_REQ_EMAIL) and the TUI already fell back to it. Since build_client_full() always writes that subject field whenever an email is known, metadata.json was a redundant, driftable copy — remove it and have get_email() read index.txt directly, picking the last non-revoked entry for a CN since index.txt can contain revoked/duplicate lines for the same CN. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,6 @@ def _patch_issue(monkeypatch, ovpn_path="/out/cn_2026-01-01/client.ovpn",
|
||||
"""Patch all side-effectful callables used by CliRunner._issue."""
|
||||
monkeypatch.setattr("openvpncertupdate.revoke_issued", MagicMock())
|
||||
monkeypatch.setattr("openvpncertupdate.build_client_full", MagicMock())
|
||||
monkeypatch.setattr("openvpncertupdate.save_email", MagicMock())
|
||||
monkeypatch.setattr("openvpncertupdate.build_ovpn", MagicMock(return_value=ovpn_path))
|
||||
monkeypatch.setattr("openvpncertupdate.create_note", MagicMock(return_value=one_time_url))
|
||||
monkeypatch.setattr("openvpncertupdate.send_email", MagicMock())
|
||||
@@ -23,7 +22,6 @@ def _patch_issue(monkeypatch, ovpn_path="/out/cn_2026-01-01/client.ovpn",
|
||||
return {
|
||||
"revoke_issued": sys.modules["openvpncertupdate"].revoke_issued,
|
||||
"build_client_full": sys.modules["openvpncertupdate"].build_client_full,
|
||||
"save_email": sys.modules["openvpncertupdate"].save_email,
|
||||
"build_ovpn": sys.modules["openvpncertupdate"].build_ovpn,
|
||||
"create_note": sys.modules["openvpncertupdate"].create_note,
|
||||
"send_email": sys.modules["openvpncertupdate"].send_email,
|
||||
@@ -44,12 +42,13 @@ def test_create_calls_build_not_revoke(monkeypatch, capsys):
|
||||
assert mocks["build_client_full"].call_args.args[2] == "alice"
|
||||
|
||||
|
||||
def test_create_saves_email(monkeypatch, capsys):
|
||||
def test_create_passes_email_to_build_client_full(monkeypatch, capsys):
|
||||
# build-client-full embeds email into the cert subject (EASYRSA_REQ_EMAIL),
|
||||
# which is what index.txt / get_email() reads back later — there's no
|
||||
# separate email store to save to.
|
||||
mocks = _patch_issue(monkeypatch)
|
||||
CliRunner().create("alice", "alice@example.com")
|
||||
mocks["save_email"].assert_called_once()
|
||||
assert mocks["save_email"].call_args.args[1] == "alice"
|
||||
assert mocks["save_email"].call_args.args[2] == "alice@example.com"
|
||||
assert mocks["build_client_full"].call_args.kwargs["email"] == "alice@example.com"
|
||||
|
||||
|
||||
def test_create_sends_email_and_prints_url(monkeypatch, capsys):
|
||||
@@ -88,13 +87,15 @@ def test_reissue_calls_revoke_then_build(monkeypatch, capsys):
|
||||
assert mocks["build_client_full"].call_args.args[2] == "bob"
|
||||
|
||||
|
||||
def test_reissue_falls_back_to_stored_email(monkeypatch, capsys):
|
||||
def test_reissue_falls_back_to_index_txt_email(monkeypatch, capsys):
|
||||
# get_email() reads straight from index.txt; this stubs that lookup
|
||||
# rather than the file itself (index.txt parsing is covered in test_pki.py).
|
||||
mocks = _patch_issue(monkeypatch)
|
||||
monkeypatch.setattr("openvpncertupdate.get_email",
|
||||
MagicMock(return_value="stored@example.com"))
|
||||
MagicMock(return_value="fromindex@example.com"))
|
||||
CliRunner().reissue("bob", "") # no --email supplied
|
||||
mocks["send_email"].assert_called_once()
|
||||
assert mocks["send_email"].call_args.args[0] == "stored@example.com"
|
||||
assert mocks["send_email"].call_args.args[0] == "fromindex@example.com"
|
||||
|
||||
|
||||
def test_reissue_no_email_no_send(monkeypatch, capsys):
|
||||
@@ -102,8 +103,9 @@ def test_reissue_no_email_no_send(monkeypatch, capsys):
|
||||
monkeypatch.setattr("openvpncertupdate.get_email", MagicMock(return_value=""))
|
||||
CliRunner().reissue("bob", "")
|
||||
mocks["send_email"].assert_not_called()
|
||||
out = capsys.readouterr().out
|
||||
out, err = capsys.readouterr()
|
||||
assert "config:" in out
|
||||
assert "warning: email skipped (no email address on file for bob)" in err
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user