diff options
| author | Anders Betts <anders.betts@gmail.com> | 2026-09-23 10:08:44 +0200 |
|---|---|---|
| committer | Anders Betts <anders.betts@gmail.com> | 2026-09-23 10:08:44 +0200 |
| commit | e06248ef69b33d102a57aaa31fffcde3240dad9c (patch) | |
| tree | d2b82eb5a5e2918336f89e241efdeed8bed5ca95 /tests/test_core.c | |
| parent | 79b27457125dc2e259783359085021eea1d26e3a (diff) | |
| download | bokf-e06248ef69b33d102a57aaa31fffcde3240dad9c.tar.gz bokf-e06248ef69b33d102a57aaa31fffcde3240dad9c.zip | |
user.set_password: change your own password; TUI "Byt lösenord"v0.1.68
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 <noreply@anthropic.com>
Diffstat (limited to 'tests/test_core.c')
| -rw-r--r-- | tests/test_core.c | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/tests/test_core.c b/tests/test_core.c index 071b5ce..6376517 100644 --- a/tests/test_core.c +++ b/tests/test_core.c @@ -1309,6 +1309,101 @@ static void test_tokens(struct tctx *t) yyjson_doc_free(d); } +static void set_pw(const char *sess, const char *cur, const char *pw, + const char *want_code) +{ + yyjson_doc *d = call(reqf( + "{\"v\":1,\"id\":\"pw\",\"cmd\":\"user.set_password\"," + "\"session\":\"%s\",\"args\":{\"current_password\":\"%s\"," + "\"new_password\":\"%s\"}}", + sess, cur, pw)); + if (want_code) + CHECK_STR(d, "error.code", want_code); + else + CHECK_OK(d); + yyjson_doc_free(d); +} + +static void test_set_password(struct tctx *t) +{ + yyjson_doc *d; + CHECK(login("admin", "secret123")); + d = call(reqf("{\"v\":1,\"id\":\"p1\",\"cmd\":\"user.create\"," + "\"session\":\"%s\",\"args\":{\"username\":\"pwuser\"," + "\"password\":\"oldpassword1\"}}", + g_session)); + CHECK_OK(d); + yyjson_doc_free(d); + + /* two sessions of the same user */ + CHECK(login("pwuser", "oldpassword1")); + char other[128]; + snprintf(other, sizeof other, "%s", g_session); + CHECK(login("pwuser", "oldpassword1")); + char mine[128]; + snprintf(mine, sizeof mine, "%s", g_session); + + set_pw(mine, "wrongpassword", "newpassword22", "AUTH_FAILED"); + set_pw(mine, "oldpassword1", "short", "INVALID_ARGS"); + set_pw(mine, "oldpassword1", "oldpassword1", "INVALID_ARGS"); + d = call(reqf("{\"v\":1,\"id\":\"p2\",\"cmd\":\"user.set_password\"," + "\"session\":\"%s\",\"args\":{\"new_password\":" + "\"newpassword22\"}}", + mine)); + CHECK_STR(d, "error.code", "INVALID_ARGS"); + yyjson_doc_free(d); + /* nothing changed yet: the other session and the old password work */ + d = call(reqf("{\"v\":1,\"id\":\"p3\",\"cmd\":\"session.whoami\"," + "\"session\":\"%s\"}", other)); + CHECK_OK(d); + yyjson_doc_free(d); + + d = call(reqf("{\"v\":1,\"id\":\"p4\",\"cmd\":\"user.set_password\"," + "\"session\":\"%s\",\"args\":{\"current_password\":" + "\"oldpassword1\",\"new_password\":\"newpassword22\"}}", + mine)); + CHECK_OK(d); + CHECK(jint(d, "result.sessions_closed") == 1); + yyjson_doc_free(d); + /* other sessions are closed, this one stays */ + d = call(reqf("{\"v\":1,\"id\":\"p5\",\"cmd\":\"session.whoami\"," + "\"session\":\"%s\"}", other)); + CHECK_STR(d, "error.code", "SESSION_EXPIRED"); + yyjson_doc_free(d); + d = call(reqf("{\"v\":1,\"id\":\"p6\",\"cmd\":\"session.whoami\"," + "\"session\":\"%s\"}", mine)); + CHECK_OK(d); + CHECK_STR(d, "result.user.username", "pwuser"); + yyjson_doc_free(d); + CHECK(!login("pwuser", "oldpassword1")); + CHECK(login("pwuser", "newpassword22")); + snprintf(mine, sizeof mine, "%s", g_session); + + /* wrong current passwords are rate limited like logins */ + for (int i = 0; i < 5; i++) + set_pw(mine, "wrongpassword", "otherpassword3", "AUTH_FAILED"); + set_pw(mine, "newpassword22", "otherpassword3", "RATE_LIMITED"); + + /* a token session cannot change the password */ + CHECK(login("admin", "secret123")); + d = call(reqf("{\"v\":1,\"id\":\"p7\",\"cmd\":\"token.create\"," + "\"session\":\"%s\",\"org\":%d,\"args\":" + "{\"label\":\"pw-test\",\"scopes\":[\"read\"]}}", + g_session, (int)t->org_id)); + CHECK_OK(d); + char tok[128]; + snprintf(tok, sizeof tok, "%s", jstr(d, "result.token")); + yyjson_doc_free(d); + d = call(reqf("{\"v\":1,\"id\":\"p8\",\"cmd\":\"session.open\",\"args\":" + "{\"method\":\"token\",\"token\":\"%s\"}}", tok)); + CHECK_OK(d); + char tsess[128]; + snprintf(tsess, sizeof tsess, "%s", jstr(d, "result.session")); + yyjson_doc_free(d); + set_pw(tsess, "secret123", "anotherpassword4", "FORBIDDEN"); + CHECK(login("admin", "secret123")); +} + static void test_accounts(struct tctx *t) { yyjson_doc *d; @@ -5801,6 +5896,7 @@ static const struct ttest TESTS[] = { { "session_auth", test_session_auth, "" }, { "org_members", test_org_members, "session_auth" }, { "tokens", test_tokens, "org_members" }, + { "set_password", test_set_password, "org_members" }, { "accounts", test_accounts, "org_members" }, { "fiscal_years", test_fiscal_years, "org_members" }, { "vouchers", test_vouchers, "org_members" }, |
