The family gate (decisions 63/64, R-780): family members with their own logins, a permanent forwardAuth door per family app, anchored exceptions, min_controller
gates / gates (push) Successful in 27s
gates / gates (push) Successful in 27s
- internal/family: the family list (bcrypt, generated 4x4 passwords shown once) + 30-day sessions in family.json (0600, atomic); a reset (generation), a removal or a logout ends sessions at the next request. - internal/stacks/family_gate.go: family_gate / family_gate_except / min_controller in .felhom.yml; the door is written BEFORE the first start (install and a removed app's restore), a life record in app.yaml, reconciled by the gate loop; priority below the install hold, setup gate and sign-up block; every exception anchored ^/prefix(/|$) (finding F1). - internal/web/family_gate.go: forwardAuth /__felhom_gate/family (app cookie felhom_famgate, host-only, names a store session); /__family/start|login|logout on the dashboard host (session cookie felhom_family, Path=/__family); sign-in counted per visitor (clientIP) AND per name, short windows; the household's dashboard session vouches. RequireAuth never reads a family cookie. The "Család" card on the security page: add / new password / remove. Red-proofs RP-F1..RP-F7 (felhom.eu audits/family-gate-2026-10-02/A/). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/family"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
@@ -48,11 +49,13 @@ type Server struct {
|
||||
version string
|
||||
encKey []byte // AES-256 key for decrypting app.yaml values
|
||||
// gate / gateClock (v0.280.0, decision 46): the setup gate's key + used tokens; the clock is a test seam.
|
||||
gate gateState
|
||||
gateClock func() time.Time
|
||||
tmpl *template.Template // the Hungarian set (i18n.Default) — every pre-i18n caller renders this
|
||||
tmplByLang map[string]*template.Template
|
||||
i18n *i18n.Bundle
|
||||
gate gateState
|
||||
fam familyState // v0.287.0: the family gate (family_gate.go)
|
||||
familyStoreOverride *family.Store // test seam: the family list without a data dir
|
||||
gateClock func() time.Time
|
||||
tmpl *template.Template // the Hungarian set (i18n.Default) — every pre-i18n caller renders this
|
||||
tmplByLang map[string]*template.Template
|
||||
i18n *i18n.Bundle
|
||||
|
||||
// versionPosition (v0.275.0) — where a just-restored app stands against the catalog; nil → the stack
|
||||
// manager's RestoredVersionPosition. A seam so the restore sentence is testable without a catalog.
|
||||
@@ -673,6 +676,11 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.sharingShareOffsiteHandler(w, r)
|
||||
case path == "/settings/notifications" && r.Method == http.MethodGet:
|
||||
s.settingsNotificationsPageHandler(w, r)
|
||||
// v0.287.0 (decisions 63/64): the „Család" card's member list and its three acts (session + CSRF like every POST).
|
||||
case path == "/family/members" && r.Method == http.MethodGet:
|
||||
s.familyMembersHandler(w, r)
|
||||
case strings.HasPrefix(path, "/family/members/") && r.Method == http.MethodPost:
|
||||
s.familyMemberActionHandler(w, r, strings.TrimPrefix(path, "/family/members/"))
|
||||
case path == "/settings/security" && r.Method == http.MethodGet:
|
||||
s.settingsSecurityPageHandler(w, r)
|
||||
case path == "/settings/password" && r.Method == http.MethodPost:
|
||||
@@ -862,6 +870,23 @@ func (s *Server) CatchAllMiddleware(next http.Handler) http.Handler {
|
||||
s.ServeGateAuth(w, r)
|
||||
return
|
||||
}
|
||||
if r.URL.Path == familyAuthPath { // v0.287.0 (decisions 63/64): the family gate's forwardAuth, any host
|
||||
s.ServeFamilyGateAuth(w, r)
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(host, controllerHost) {
|
||||
switch r.URL.Path { // v0.287.0: the family sign-in pages, outside the dashboard's own auth on purpose
|
||||
case familyStartPath:
|
||||
s.ServeFamilyStart(w, r)
|
||||
return
|
||||
case familyLoginPath:
|
||||
s.ServeFamilyLogin(w, r)
|
||||
return
|
||||
case familyLogoutPath:
|
||||
s.ServeFamilyLogout(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
if r.URL.Path == signupClosedPath { // v0.281.0 (decision 47): an app's own sign-up address while closed
|
||||
s.ServeSignupClosed(w, r)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user