doorstep: console is Felhom's (ISO 1.27.0 source), passphrase hand-over copy (hub 0.113.0 source), rulings
gates / gates (push) Successful in 19s
gates / gates (push) Successful in 19s
Phase 0: the public ISO never auto-installs by construction (no answer.toml, G1); the operator re-affirmed the interactive installer 2026-09-14. - felhom-bootstrap.sh: mask pvebanner.service, write a Hungarian /etc/issue (no :8006 admin URL); pairing banner names the Tulajdonosi jelmondat and paints through the CONSOLE_DEV seam (R-496). Harness: 8 checks, red first; fake hub now sends a pairing code (the banner was never tested, R-502). - hub: created flash + Credentials block tell the operator to hand the phrase over; the self-bind mail names the operator (R-497). Tests red first. - iso-release-gate G14-G16; domain ruling in 01-topology + CONTEXT; R-494 narrowed to P3; R-502..R-504 filed; volunteer guide and day-0 A.2 aligned. ISO_VERSION 1.27.0 (not built, not published). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -347,7 +347,8 @@ A hivatkozás megnyitása után két adatot kell megadnod:
|
||||
|
||||
1. A párosító kódot, amely a doboz képernyőjén (a monitoron) látható.
|
||||
2. A tulajdonosi jelmondatodat (az 5 szóból álló kifejezést), amelyet a
|
||||
beállításkor kaptál. Ez igazolja, hogy a fiók a tiéd.
|
||||
Felhom üzemeltetőjétől kaptál — személyesen vagy telefonon, e-mailben
|
||||
soha. Ez igazolja, hogy a fiók a tiéd.
|
||||
|
||||
A hivatkozás 7 napig érvényes. Biztonsági okból 5 sikertelen próbálkozás után
|
||||
zárolódik — ilyenkor vedd fel a kapcsolatot az ügyfélszolgálattal.
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package web
|
||||
|
||||
// R-497 — the „Tulajdonosi jelmondat" is delivered by NOTHING in the product: it is minted at customer
|
||||
// creation, shown only on the operator's customer page, and never e-mailed (the operator has not ruled
|
||||
// that e-mail may carry it). Measured 2026-09-14 in the first-hour drill: the self-bind mail told the
|
||||
// customer they had received it „a beállításkor" — true only if the operator remembered to hand it over,
|
||||
// and nothing told the operator to.
|
||||
//
|
||||
// These pin the CONSEQUENCE, not a string for its own sake: the operator is told at the moment the
|
||||
// phrase exists, the page repeats it where the phrase is shown, and the customer's mail names the person
|
||||
// they got it from. The last test pins what must NOT change — the mail still carries no phrase.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/notify"
|
||||
)
|
||||
|
||||
// handoverMarker is the operator-facing instruction. One sentence, rendered in two places.
|
||||
const handoverMarker = "Hand this phrase to the customer now"
|
||||
|
||||
func TestCustomerPage_CreatedFlashTellsTheOperatorToHandOverThePhrase(t *testing.T) {
|
||||
s, st := newTestServer(t)
|
||||
seedCustomer(t, st, "acme", "")
|
||||
|
||||
created := renderCustomerPageWithQuery(t, s, "acme", "?flash=created")
|
||||
if !strings.Contains(created, handoverMarker) {
|
||||
t.Errorf("the customer-created flash does not tell the operator to hand over the phrase:\n%s", created)
|
||||
}
|
||||
if !strings.Contains(created, "never e-mailed") {
|
||||
t.Error("the created flash must say the phrase is never e-mailed — otherwise the operator waits for a mail")
|
||||
}
|
||||
|
||||
// Nominal page (no flash): the instruction stays beside the phrase, where the operator reveals it.
|
||||
plain := renderCustomerPage(t, s, "acme")
|
||||
if !strings.Contains(plain, handoverMarker) {
|
||||
t.Error("the Credentials block does not repeat the hand-over sentence beside the phrase")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfBindEmail_SaysWhoHandedOverThePhrase(t *testing.T) {
|
||||
_, body := notify.FormatSelfBindEmail("acme", "https://hub.example/bind/tok")
|
||||
|
||||
if strings.Contains(body, "beállításkor kaptál") {
|
||||
t.Error("the mail still says the customer received the phrase „a beállításkor” — nothing delivers it then")
|
||||
}
|
||||
if !strings.Contains(body, "üzemeltetőjétől") {
|
||||
t.Errorf("the mail must name who hands over the phrase (the Felhom operator):\n%s", body)
|
||||
}
|
||||
// Unchanged contract: one name, and what the thing is.
|
||||
if !strings.Contains(body, "tulajdonosi jelmondatodat") || !strings.Contains(body, "5 szóból álló kifejezést") {
|
||||
t.Error("the mail lost the secret's name or its description")
|
||||
}
|
||||
}
|
||||
|
||||
// The phrase itself must never ride the mail. A customer with a seeded phrase gets a mail that does not
|
||||
// contain it — the builder takes no phrase argument today; this pins that no future edit adds one via
|
||||
// the link text or a helper.
|
||||
func TestSelfBindEmail_NeverCarriesThePhrase(t *testing.T) {
|
||||
const phrase = "alma korte szilva barack meggy"
|
||||
_, body := notify.FormatSelfBindEmail("acme", "https://hub.example/bind/tok")
|
||||
for _, w := range strings.Fields(phrase) {
|
||||
if strings.Contains(body, w) {
|
||||
t.Errorf("the self-bind mail contains a passphrase word %q", w)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
{{if .Flash}}
|
||||
<div class="flash flash-success">
|
||||
{{if eq .Flash "created"}}Configuration created successfully.
|
||||
{{if eq .Flash "created"}}Configuration created successfully. <strong>Hand this phrase to the customer now</strong> — the Retrieval Password below is the customer's „Tulajdonosi jelmondat”. Give it in person or by phone; it is never e-mailed, and the self-bind page asks for it.
|
||||
{{else if eq .Flash "updated"}}Configuration updated.
|
||||
{{else if eq .Flash "password_regenerated"}}Retrieval password regenerated.
|
||||
{{else if eq .Flash "offsite_reissued"}}Offsite credentials re-issued — a fresh one-time password is staged; the controller picks it up on its next config refresh.
|
||||
@@ -464,6 +464,7 @@
|
||||
<button type="button" class="copy-btn" onclick="copySecret('retrieval-pw')" title="Copy">⎘</button>
|
||||
</div>
|
||||
<span class="form-hint">The per-customer secret that fetches the whole config — masked by default; never place it on a command line (the installer reads it at a no-echo prompt).</span>
|
||||
<span class="form-hint"><strong>Hand this phrase to the customer now</strong> — in person or by phone. The customer knows it as „Tulajdonosi jelmondat”; it is never e-mailed, and the self-bind page asks for it (R-497).</span>
|
||||
</div>
|
||||
<form method="POST" action="/configs/{{.CustomerID}}/regen-password" style="margin-top: 0.5rem;">
|
||||
{{.CSRFField}}
|
||||
|
||||
Reference in New Issue
Block a user