controller: customer-claim password gate v0.122.0 (closes DRILL-day0-vm F-4/F-5)
The customer sets + owns the dashboard password via a hub-emailed one-time claim code. An unclaimed box (code hash present, no password) serves ONLY the claim page — every other route → claim page (302) or 401, so a Day-0 box is never open on the internet. A set password disables the gate (auth wins). Reset rides the same code engine (login "Elfelejtett jelszó"). Legacy-open (no password, no hash) shows a red transition banner until the hub delivers a hash. Report ACK caches the code state idempotently by generation; report carries claimed (set-only). --print-reset-code root escape hatch. Requires hub v0.50.0. Gate-coverage signature test + 4 red-proofs proven.
This commit is contained in:
@@ -51,7 +51,24 @@ func (s *Server) authEnabled() bool {
|
||||
// RequireAuth returns middleware that checks for valid session or shows login.
|
||||
func (s *Server) RequireAuth(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Skip auth if no password is configured
|
||||
// Customer-claim gate (v0.122.0, F-4): an unclaimed box with a delivered code hash and no
|
||||
// password serves ONLY the claim page + its assets; everything else → claim page / 401.
|
||||
// The claim routes (/claim, /claim/request-new-code) are handled by the mux — let them
|
||||
// through so serveClaimGate only intercepts the GATED paths. A set password disables the
|
||||
// gate entirely (claimGateActive returns false → the normal auth path below runs).
|
||||
if s.claimGateActive() {
|
||||
if claimPageAllowedPath(r.URL.Path) {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] [web] claim gate: intercepting %s %s (unclaimed)", r.Method, r.URL.Path)
|
||||
}
|
||||
s.serveClaimGate(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Skip auth if no password is configured (legacy-open transition state, or claim disabled).
|
||||
if !s.authEnabled() {
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] [web] auth: no password configured, passing through %s %s", r.Method, r.URL.Path)
|
||||
@@ -65,6 +82,13 @@ func (s *Server) RequireAuth(next http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
// Claim/reset routes stay reachable pre-auth even on a claimed box: they are the RESET
|
||||
// entry (code-gated internally). Static assets for the page too.
|
||||
if r.URL.Path == "/claim" || r.URL.Path == "/claim/request-new-code" || strings.HasPrefix(r.URL.Path, "/static/") {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Path == "/login" && r.Method == http.MethodPost {
|
||||
s.handleLogin(w, r)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user