v0.325.0: share + escrow limiters reserve first; family sessions as fingerprints; samba renames apply; path guard widened; gates in a worktree
gates / gates (push) Successful in 45s
gates / gates (push) Successful in 45s
R-1111 reserveAttempt is the one limiter body (share password, escrow re-auth, dashboard sign-in). R-1112 family.json holds fingerprints; old files are converted at load. R-1113 the household's pass reuses its family session. R-1109 the samba compose carries the smb.conf hash. R-1110 TestNoBareDrivePathToPathHelpers. R-1114 shared_scripts.py resolves the felhom.eu sibling from a worktree. Each red-proved. Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
1 parent
7d41c2973c
commit
94f44931dd
28 files changed
+945
-190
No files matched your search
@@ -158,8 +158,10 @@ func (s *Server) familyRDHost(rd string) (string, bool) {
|
||||
// family-gated app's backend receives its cookies, and a session id lifted from one app, set as the family session
|
||||
// cookie on the dashboard host, minted passes into EVERY family app for 30 days. A derived token cannot be turned back
|
||||
// into the session, and it opens only its own host. Pinned by TestR976_AnAppCookieCannotBeLiftedIntoAnotherApp.
|
||||
func (s *Server) familyAppToken(host, sid string) string {
|
||||
return s.gateMAC("famapp", host, sid)
|
||||
// R-1112: it is derived from the session's FINGERPRINT (fp) — all the store keeps — so app cookies minted before
|
||||
// R-1112 (derived from the value) are refused once and the browser passes through the sign-in start again.
|
||||
func (s *Server) familyAppToken(host, fp string) string {
|
||||
return s.gateMAC("famapp", host, fp)
|
||||
}
|
||||
|
||||
// familySessionForAppToken finds the session an app token was derived from (still valid, as Store.Valid judges it).
|
||||
@@ -167,8 +169,8 @@ func (s *Server) familySessionForAppToken(host, at string) (family.Session, bool
|
||||
if at == "" {
|
||||
return family.Session{}, false
|
||||
}
|
||||
return s.familyStore().Find(func(id string) bool {
|
||||
return hmac.Equal([]byte(s.familyAppToken(host, id)), []byte(at))
|
||||
return s.familyStore().Find(func(fp string) bool {
|
||||
return hmac.Equal([]byte(s.familyAppToken(host, fp)), []byte(at))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -181,10 +183,10 @@ type familyToken struct {
|
||||
MAC string `json:"m"`
|
||||
}
|
||||
|
||||
func (s *Server) mintFamilyToken(host, sid, rd string) string {
|
||||
func (s *Server) mintFamilyToken(host, fp, rd string) string {
|
||||
nb := make([]byte, 12)
|
||||
_, _ = rand.Read(nb)
|
||||
t := familyToken{Host: host, App: s.familyAppToken(host, sid), Exp: s.gateNow().Add(gateTokenLife).Unix(), Nonce: hex.EncodeToString(nb), RD: rd}
|
||||
t := familyToken{Host: host, App: s.familyAppToken(host, fp), Exp: s.gateNow().Add(gateTokenLife).Unix(), Nonce: hex.EncodeToString(nb), RD: rd}
|
||||
t.MAC = s.gateMAC("ftoken", t.Host, t.App, strconv.FormatInt(t.Exp, 10), t.Nonce, t.RD)
|
||||
b, _ := json.Marshal(t)
|
||||
return base64.RawURLEncoding.EncodeToString(b)
|
||||
@@ -334,16 +336,18 @@ func (s *Server) ServeFamilyStart(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
se, ok := s.familySessionFromCookie(r)
|
||||
if !ok && s.hasSession(r) {
|
||||
// The household's own dashboard session vouches (decision 46's rule) — a household session in the family store,
|
||||
// never the dashboard session itself.
|
||||
// The household's own dashboard session vouches (decision 46's rule) — the household's session in the family
|
||||
// store, never the dashboard session itself. R-1113: its live one, not a new one on every pass.
|
||||
if st := s.familyStore(); st != nil {
|
||||
if sid, err := st.NewSession(""); err == nil {
|
||||
se, ok = family.Session{ID: sid}, true
|
||||
if hs, err := st.HouseholdSession(); err == nil {
|
||||
se, ok = hs, true
|
||||
} else {
|
||||
s.logger.Printf("[ERROR] [web] family start: the household's family session could not be saved: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
http.Redirect(w, r, "https://"+host+familyCallbackURI+"?"+url.Values{"t": {s.mintFamilyToken(host, se.ID, rd)}}.Encode(), http.StatusFound)
|
||||
http.Redirect(w, r, "https://"+host+familyCallbackURI+"?"+url.Values{"t": {s.mintFamilyToken(host, se.FP, rd)}}.Encode(), http.StatusFound)
|
||||
return
|
||||
}
|
||||
s.renderFamilyPage(w, r, rd, "", http.StatusOK)
|
||||
@@ -409,9 +413,9 @@ func (s *Server) ServeFamilyLogin(w http.ResponseWriter, r *http.Request) {
|
||||
s.renderFamilyPage(w, r, "", s.msg(r, "family_gate.msg.signed_in"), http.StatusOK)
|
||||
}
|
||||
|
||||
// familyLogoutToken is the sign-out form's token, bound to the family session it ends (R-983, v0.324.0). The sign-in
|
||||
// form's token (familyFormToken) is the same for every visitor, so a stranger could fetch one and use it; this one
|
||||
// names the session, which another page never sees.
|
||||
// familyLogoutToken is the sign-out form's token, bound to the family session it ends (R-983, v0.324.0) — to the
|
||||
// cookie value the browser holds (sid). The sign-in form's token (familyFormToken) is the same for every visitor, so a
|
||||
// stranger could fetch one and use it; this one names the session, which another page never sees.
|
||||
func (s *Server) familyLogoutToken(sid string) string {
|
||||
return s.gateMAC("famlogout", sid)
|
||||
}
|
||||
@@ -448,9 +452,10 @@ func (s *Server) renderFamilyPage(w http.ResponseWriter, r *http.Request, rd, no
|
||||
}
|
||||
}
|
||||
}
|
||||
if se, ok := s.familySessionFromCookie(r); ok {
|
||||
if _, ok := s.familySessionFromCookie(r); ok {
|
||||
c, _ := r.Cookie(familySessionCookie) // present: familySessionFromCookie found it
|
||||
data["SignedIn"] = true
|
||||
data["LogoutToken"] = s.familyLogoutToken(se.ID)
|
||||
data["LogoutToken"] = s.familyLogoutToken(c.Value)
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
|
||||
Reference in new issue
Block a user