010-auth.js 4.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /*
  2. Critical path: sign in / sign out.
  3. Drives the real login.html form and the auth API against a live
  4. ArozOS server: page render, credential rejection, successful login,
  5. session persistence, auth-gated redirects and logout.
  6. */
  7. "use strict";
  8. const h = require("../lib/system-harness");
  9. function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
  10. h.run("AUTH", async function (env) {
  11. const base = env.baseURL;
  12. const admin = env.admin;
  13. // ── 1. Login page renders with the expected form controls ──
  14. let page = await h.newPage(env.browser);
  15. await page.goto(base + "/login.html", { waitUntil: "domcontentloaded" });
  16. if (!(await page.isVisible("#username"))) h.fail("login page: username field not visible");
  17. if (!(await page.isVisible("#magic"))) h.fail("login page: password field not visible");
  18. if (!(await page.isVisible("#loginbtn"))) h.fail("login page: sign-in button not visible");
  19. h.ok("login page renders username/password fields and sign-in button");
  20. // ── 2. Auth-gated pages redirect anonymous visitors to login ──
  21. await page.goto(base + "/desktop.html", { waitUntil: "domcontentloaded" });
  22. if (page.url().indexOf("login.html") === -1) {
  23. h.fail("anonymous visit to desktop.html was not redirected to login.html (got " + page.url() + ")");
  24. }
  25. h.ok("anonymous visit to desktop.html redirects to the login page");
  26. // ── 3. Bogus credentials are rejected ──
  27. await page.goto(base + "/login.html", { waitUntil: "domcontentloaded" });
  28. await page.fill("#username", "no-such-user");
  29. await page.fill("#magic", "definitely-wrong");
  30. await page.click("#loginbtn");
  31. await sleep(1500); // give the form's ajax round-trip time to finish
  32. if (page.url().indexOf("login.html") === -1) h.fail("bogus credentials left the login page");
  33. if (await h.isLoggedIn(page, base)) h.fail("bogus credentials produced a session");
  34. h.ok("bogus credentials are rejected and no session is created");
  35. // ── 4. Real login through the form lands on the desktop ──
  36. await h.loginViaForm(page, base, admin.username, admin.password);
  37. if (!(await h.isLoggedIn(page, base))) h.fail("form login did not create a session");
  38. await page.goto(base + "/", { waitUntil: "domcontentloaded" });
  39. if (page.url().indexOf("desktop.html") === -1) {
  40. h.fail("logged-in visit to / did not land on desktop.html (got " + page.url() + ")");
  41. }
  42. h.ok("form login succeeds and / lands on desktop.html");
  43. // ── 5. Session persists across pages in the same browser context ──
  44. const page2 = await page.context().newPage();
  45. await page2.goto(base + "/", { waitUntil: "domcontentloaded" });
  46. if (page2.url().indexOf("desktop.html") === -1) h.fail("session did not persist to a second page");
  47. await page2.close();
  48. h.ok("session persists across pages in the same context");
  49. // ── 6. Logout kills the session; auth-gated pages redirect again ──
  50. // Leave the desktop first so its background pollers cannot race the
  51. // logout and re-write the session cookie in the browser's jar.
  52. await page.goto("about:blank");
  53. await h.logout(page, base);
  54. if (await h.isLoggedIn(page, base)) h.fail("logout left the session alive");
  55. // Query param busts the browser HTTP cache - desktop.html was cached
  56. // during the logged-in visit and would otherwise never hit the server.
  57. await page.goto(base + "/desktop.html?after_logout=1", { waitUntil: "domcontentloaded" });
  58. if (page.url().indexOf("login.html") === -1) h.fail("post-logout visit to desktop.html did not land on login.html");
  59. h.ok("logout destroys the session and the desktop redirects to login again");
  60. await page.close();
  61. // ── 7. Wrong password for a real account (fresh context) ──
  62. page = await h.newPage(env.browser);
  63. const res = await page.request.post(base + "/system/auth/login", {
  64. form: { username: admin.username, password: "wrong-password", rmbme: "false" }
  65. });
  66. const body = (await res.text()).trim();
  67. if (await h.isLoggedIn(page, base)) h.fail("wrong password for real account produced a session");
  68. if (body.indexOf("error") === -1) h.fail("wrong-password login did not return an error: " + body);
  69. h.ok("wrong password for a real account is rejected");
  70. // The exponential login-delay counter now blocks this user/IP pair for
  71. // ~2s; wait it out, then confirm the correct password works again.
  72. await sleep(3000);
  73. await h.loginViaAPI(page, base, admin.username, admin.password);
  74. h.ok("correct password logs in again after the failed-attempt delay");
  75. // ── 8. checkLogin reflects the API session state ──
  76. if (!(await h.isLoggedIn(page, base))) h.fail("checkLogin false after API login");
  77. await h.logout(page, base);
  78. if (await h.isLoggedIn(page, base)) h.fail("checkLogin true after logout");
  79. h.ok("checkLogin correctly tracks login and logout");
  80. await page.close();
  81. });