R-415 fixed on hub main (no duplicate/nested/felhom.eu customer domain; R-138 option B); R-762 closed (9202 proven, catalog eec9a0d); R-905 opened (wger static in backups); decision sheet D10; 130 -> 129
gates / gates (push) Successful in 3m55s
gates / gates (push) Successful in 3m55s
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:
@@ -741,6 +741,18 @@ func (s *Server) handleConfigCreate(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// R-415 / R-138 option B: a domain equal to, inside or containing another customer's, or under felhom.eu, is refused
|
||||
// BEFORE anything is generated or provisioned.
|
||||
if msg := s.domainConflictMessage(customerID, r.FormValue("domain")); msg != "" {
|
||||
s.renderConfigForm(w, r, true, &store.CustomerConfig{
|
||||
CustomerID: customerID,
|
||||
CustomerName: r.FormValue("customer_name"),
|
||||
Domain: r.FormValue("domain"),
|
||||
Email: r.FormValue("email"),
|
||||
}, nil, msg)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate credentials.
|
||||
//
|
||||
// R-597: the Owner passphrase follows the language the operator is choosing ON THIS FORM, not
|
||||
@@ -861,6 +873,13 @@ func (s *Server) handleConfigUpdate(w http.ResponseWriter, r *http.Request, cust
|
||||
s.renderConfigForm(w, r, false, cfg, submitted, "Display Name and Domain are required.")
|
||||
return
|
||||
}
|
||||
// R-415 / R-138 option B — the same guard on edit (a customer's own current domain never conflicts with itself).
|
||||
if msg := s.domainConflictMessage(customerID, cfg.Domain); msg != "" {
|
||||
var submitted map[string]interface{}
|
||||
_ = json.Unmarshal([]byte(buildConfigJSON(r)), &submitted)
|
||||
s.renderConfigForm(w, r, false, cfg, submitted, msg)
|
||||
return
|
||||
}
|
||||
|
||||
cfg.ConfigJSON = buildConfigJSON(r)
|
||||
|
||||
@@ -1769,3 +1788,22 @@ func (s *Server) handleGeoDisable(w http.ResponseWriter, r *http.Request, custom
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "message": "Geo-restriction removed from Cloudflare."})
|
||||
}
|
||||
|
||||
// domainConflictMessage is the operator's sentence for a refused domain ("" = allowed). A store error refuses too
|
||||
// (fail closed: the save can be retried; a duplicate domain cannot be undone once boxes use it).
|
||||
func (s *Server) domainConflictMessage(customerID, domain string) string {
|
||||
other, err := s.store.DomainConflict(customerID, domain)
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERROR] domain check for %s failed: %v — save refused", customerID, err)
|
||||
return "The domain could not be checked against the other customers — nothing was saved. Try again."
|
||||
}
|
||||
switch other {
|
||||
case "":
|
||||
return ""
|
||||
case "felhom.eu":
|
||||
return fmt.Sprintf("Domain %q is under felhom.eu — every customer has their own domain (01 §7). Nothing was saved.", strings.TrimSpace(domain))
|
||||
default:
|
||||
s.logger.Printf("[WARN] domain %q for %s refused: it overlaps customer %s's domain (R-415)", strings.TrimSpace(domain), customerID, other)
|
||||
return fmt.Sprintf("Domain %q equals, contains or lies under the domain of customer %q — every customer has their own domain (01 §7). Nothing was saved.", strings.TrimSpace(domain), other)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user