fix: cursor visibility, Left/Right button nav, expand banned password chars

- Fix cursor not appearing in Certificate Details text fields (hint addstr
  was clobbering the cursor position set by place_cursor before refresh)
- Add Left/Right arrow key switching between Continue and Cancel buttons
- Expand password generation banned characters from oO01lI to oO01lIQ5S2Z8B
  (adds visually ambiguous pairs: Q/0, 5/S, 2/Z, 8/B)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad Doloman
2026-06-24 14:47:56 +03:00
parent 6ed7f9feef
commit 24ee8fc347
3 changed files with 14 additions and 8 deletions

View File

@@ -81,6 +81,6 @@ python3 -m pytest tests/test_pki.py::test_sorted_ascending -v # single test
- EasyRSA called with `--batch`; `--passin=pass:<CA_PASSPHRASE>` omitted when `CA_PASSPHRASE=""` - EasyRSA called with `--batch`; `--passin=pass:<CA_PASSPHRASE>` omitted when `CA_PASSPHRASE=""`
- Cryptgeon: `raw_key=os.urandom(32)`, `aes_key=SHA-256(raw_key)`, AES-256-GCM, URL fragment=`base64url(raw_key)` - Cryptgeon: `raw_key=os.urandom(32)`, `aes_key=SHA-256(raw_key)`, AES-256-GCM, URL fragment=`base64url(raw_key)`
- `copy_crl()` does `chmod 644` after copy - `copy_crl()` does `chmod 644` after copy
- Password: pos 1=uppercase, pos 2=lowercase (no j), pos 3-27=alphanumeric, pos 28=lowercase (no j); `oO01lI` banned everywhere - Password: pos 1=uppercase, pos 2=lowercase (no j), pos 3-27=alphanumeric, pos 28=lowercase (no j); `oO01lIQ5S2Z8B` banned everywhere
- Inline file path: `<PKI_DIR>/inline/private/<CN>.inline` - Inline file path: `<PKI_DIR>/inline/private/<CN>.inline`
- User emails stored in `<PKI_DIR>/openvpncertupdate-metadata.json` - User emails stored in `<PKI_DIR>/openvpncertupdate-metadata.json`

View File

@@ -154,7 +154,7 @@ def load_all_certs(pki_dir: str) -> list[CertInfo]:
# === PASSWORD === # === PASSWORD ===
# ============================================================ # ============================================================
_BANNED_ALL = frozenset("oO01lI") _BANNED_ALL = frozenset("oO01lIQ5S2Z8B")
_BANNED_POS2_LAST = _BANNED_ALL | frozenset("j") _BANNED_POS2_LAST = _BANNED_ALL | frozenset("j")
_UPPER = [c for c in string.ascii_uppercase if c not in _BANNED_ALL] _UPPER = [c for c in string.ascii_uppercase if c not in _BANNED_ALL]
@@ -636,6 +636,12 @@ def show_cert_form(
except curses.error: except curses.error:
pass pass
hint = "Tab=next F5=regen pwd Ctrl-G=confirm Esc=cancel"
try:
win.addstr(h - 2, 2, hint[:w - 4], curses.color_pair(COLOR_DISABLED))
except curses.error:
pass
if current == _BTN_CONTINUE: if current == _BTN_CONTINUE:
try: try:
win.move(_BTN_Y, _btn_x_cont) win.move(_BTN_Y, _btn_x_cont)
@@ -649,11 +655,6 @@ def show_cert_form(
else: else:
fields[current].place_cursor() fields[current].place_cursor()
hint = "Tab=next F5=regen pwd Ctrl-G=confirm Esc=cancel"
try:
win.addstr(h - 2, 2, hint[:w - 4], curses.color_pair(COLOR_DISABLED))
except curses.error:
pass
win.refresh() win.refresh()
key = win.getch() key = win.getch()
@@ -677,6 +678,11 @@ def show_cert_form(
) )
continue continue
if current in (_BTN_CONTINUE, _BTN_CANCEL): if current in (_BTN_CONTINUE, _BTN_CANCEL):
if key in (curses.KEY_LEFT, curses.KEY_RIGHT):
focus = active.index(
_BTN_CANCEL if current == _BTN_CONTINUE else _BTN_CONTINUE
)
continue
if key in (10, 13, curses.KEY_ENTER, ord(" ")): if key in (10, 13, curses.KEY_ENTER, ord(" ")):
curses.curs_set(0) curses.curs_set(0)
if current == _BTN_CONTINUE: if current == _BTN_CONTINUE:

View File

@@ -1,6 +1,6 @@
from openvpncertupdate import generate_password from openvpncertupdate import generate_password
BANNED_ALL = set("oO01lI") BANNED_ALL = set("oO01lIQ5S2Z8B")
BANNED_POS2_LAST = BANNED_ALL | {"j"} BANNED_POS2_LAST = BANNED_ALL | {"j"}