From e06248ef69b33d102a57aaa31fffcde3240dad9c Mon Sep 17 00:00:00 2001 From: Anders Betts Date: Wed, 23 Sep 2026 10:08:44 +0200 Subject: user.set_password: change your own password; TUI "Byt lösenord" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Needs the current password (wrong ones rate limited like logins) and a password session, requires at least 10 characters, closes the user's other sessions and is audited without secrets. The TUI main menu gets "Byt lösenord" with masked prompts; ^R keeps working with the new password. Masked prompt buffers are wiped before they are freed. Co-Authored-By: Claude Opus 5.5 --- clients/screens_dashboard.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'clients/screens_dashboard.c') diff --git a/clients/screens_dashboard.c b/clients/screens_dashboard.c index 127d4df..45f0aed 100644 --- a/clients/screens_dashboard.c +++ b/clients/screens_dashboard.c @@ -275,9 +275,72 @@ static const char *const MAIN_ITEMS[] = { "Bolaget", "System", "Ingående balans", "Räkenskapsår", "Byt bolag", + "Byt lösenord", "Logga ut / avsluta", }; +#define PASSWORD_MIN_LEN 10 /* user.set_password's server-side minimum */ + +/* Changes the logged-in user's password (user.set_password). Every prompt + is masked; Esc anywhere cancels without a request. */ +static void change_password(struct app *a) +{ + char cur[128] = "", pw[128] = "", again[128] = ""; + if (!tui_prompt_into(cur, sizeof cur, "Nuvarande lösenord: ", "", 1) || + !tui_prompt_into(pw, sizeof pw, "Nytt lösenord (minst 10 tecken): ", + "", 1) || + !tui_prompt_into(again, sizeof again, "Upprepa nytt lösenord: ", "", + 1)) + goto done; + if (strlen(pw) < PASSWORD_MIN_LEN) { + tui_message("Byt lösenord", + "Det nya lösenordet måste ha minst %d tecken.", + PASSWORD_MIN_LEN); + goto done; + } + if (strcmp(pw, again) != 0) { + tui_message("Byt lösenord", "Lösenorden stämmer inte överens."); + goto done; + } + if (strcmp(pw, cur) == 0) { + tui_message("Byt lösenord", + "Det nya lösenordet måste skilja sig från det nuvarande."); + goto done; + } + yyjson_mut_doc *d = yyjson_mut_doc_new(NULL); + yyjson_mut_val *o = yyjson_mut_obj(d); + yyjson_mut_doc_set_root(d, o); + yyjson_mut_obj_add_strcpy(d, o, "current_password", cur); + yyjson_mut_obj_add_strcpy(d, o, "new_password", pw); + char *args = yyjson_mut_write(d, 0, NULL); + yyjson_mut_doc_free(d); + char *resp = client_rpc(&a->conn, "user.set_password", a->session, 0, + args); + if (args) { + memset(args, 0, strlen(args)); + free(args); + } + if (resp && client_ok(resp)) { + int64_t closed = jint_val(resp, "result.sessions_closed", 0); + /* ^R re-logs in with the kept password */ + if (a->password[0]) + snprintf(a->password, sizeof a->password, "%s", pw); + tui_message("Byt lösenord", + "Lösenordet är bytt.%s\n" + "Loggar du in via bokftui-bw: uppdatera lösenordet i " + "Bitwarden.", + closed > 0 ? " Dina andra inloggningar har loggats ut." + : ""); + } else { + show_error("Byt lösenord", resp); + } + free(resp); +done: + memset(cur, 0, sizeof cur); + memset(pw, 0, sizeof pw); + memset(again, 0, sizeof again); +} + /* Picks another org and reloads everything that belongs to the org: its context (name, role, current fiscal year, series) and the remembered list selections. */ @@ -356,6 +419,9 @@ void dashboard(struct app *a) switch_org(a); break; case 13: + change_password(a); + break; + case 14: return; default: break; -- cgit v1.3