footer sign-in link; sign-in, invite and reset land on the console
One quiet 'Sign in · leaders & families' in the public footer, after the site's own links and before Join, which stays the only call to action. Nothing on the public site pointed at /login before; the only ways in were typing the URL or an invite link. A fresh sign-in, an accepted invite and a used reset link now land on /leaders/, which sends a member on to Family and a leader to Summary. tests/smoke_identity.py 123 -> 125.
This commit is contained in:
+15
-7
@@ -266,6 +266,14 @@ kinds = {r["kind"] for r in I.list_events(limit=500)}
|
||||
check("people actions in the log", {"invite.revoked", "person.roles", "person.disabled", "person.enabled",
|
||||
"password.reset_issued", "password.reset", "password.changed"} <= kinds)
|
||||
|
||||
print("\nfooter sign-in")
|
||||
import os as _os
|
||||
_src = open(_os.path.join(_os.path.dirname(__file__), "..", "app", "app.py")).read()
|
||||
_foot = _src[_src.find("<footer"):_src.find("</footer>")]
|
||||
check("public footer carries one quiet sign-in link before Join", _foot.count('href="/login"') == 1 and _foot.index('href="/login"') < _foot.index('href="/join"'))
|
||||
check("sign-in lands on the console by default; unsafe next still refused",
|
||||
I.safe_next("") == "/leaders/" and I.safe_next("//evil") == "/leaders/" and I.safe_next("/leaders/roster") == "/leaders/roster")
|
||||
|
||||
print("\nmeeting words")
|
||||
D = dict(MEETING_DAY="Tuesday", MEETING_DAYS="Tuesdays", MEETING_DAY_ABBR="Tue", PACK_TIME="6:00 PM",
|
||||
TROOP_TIME="7:30 PM", PACK_CLOCK="6:00", TROOP_CLOCK="7:30")
|
||||
@@ -288,13 +296,13 @@ check("live seed rows reproduce the constants", I.meeting_words(I.list_units(),
|
||||
print("\nsafe_next")
|
||||
check("relative path passes", I.safe_next("/leaders/") == "/leaders/")
|
||||
check("query string kept", I.safe_next("/leaders/nearby?x=1") == "/leaders/nearby?x=1")
|
||||
check("empty falls back", I.safe_next("") == "/account")
|
||||
check("None falls back", I.safe_next(None) == "/account")
|
||||
check("absolute URL rejected", I.safe_next("https://evil.example/") == "/account")
|
||||
check("protocol-relative rejected", I.safe_next("//evil.example/") == "/account")
|
||||
check("backslash form rejected", I.safe_next("/\\evil.example") == "/account")
|
||||
check("control char rejected", I.safe_next("/leaders\r\nX: y") == "/account")
|
||||
check("no leading slash rejected", I.safe_next("leaders/") == "/account")
|
||||
check("empty falls back", I.safe_next("") == "/leaders/")
|
||||
check("None falls back", I.safe_next(None) == "/leaders/")
|
||||
check("absolute URL rejected", I.safe_next("https://evil.example/") == "/leaders/")
|
||||
check("protocol-relative rejected", I.safe_next("//evil.example/") == "/leaders/")
|
||||
check("backslash form rejected", I.safe_next("/\\evil.example") == "/leaders/")
|
||||
check("control char rejected", I.safe_next("/leaders\r\nX: y") == "/leaders/")
|
||||
check("no leading slash rejected", I.safe_next("leaders/") == "/leaders/")
|
||||
check("custom default honoured", I.safe_next("nope", default="/") == "/")
|
||||
|
||||
print("\n%d passed, %d failed" % (PASS, FAIL))
|
||||
|
||||
Reference in New Issue
Block a user