v0.280.0: the setup gate (decision 46); R-710 'I changed it' + absent-record window; R-709 password fields off the page; password:N:special generator
gates / gates (push) Successful in 25s
gates / gates (push) Successful in 25s
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:
@@ -11,6 +11,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -463,7 +464,7 @@ func (s *Server) deployHandler(w http.ResponseWriter, r *http.Request, name stri
|
||||
// v0.279.0 (decision 45): the default login, before the install when nothing will replace it, after it
|
||||
// while it is still in effect.
|
||||
data["KnownLoginLine"] = s.knownLoginLine(lang, meta, appCfg, alreadyDeployed)
|
||||
data["DefaultLoginReplaced"] = meta.AfterInstall != nil && !defaultLoginInEffect(meta, appCfg, alreadyDeployed)
|
||||
data["DefaultLoginReplaced"] = defaultLoginReplaced(meta, appCfg, alreadyDeployed)
|
||||
data["LogoURL"] = s.cfg.AppLogoURL(meta.Slug)
|
||||
data["LogoPNGURL"] = s.cfg.AppLogoPNGURL(meta.Slug)
|
||||
data["AppPageURL"] = s.cfg.AppPageURL(meta.Slug)
|
||||
@@ -500,6 +501,13 @@ func (s *Server) deployHandler(w http.ResponseWriter, r *http.Request, name stri
|
||||
delete(decryptedEnv, n)
|
||||
}
|
||||
}
|
||||
// R-709 (v0.280.0): a `type: password` value is never written into this page's HTML either — like a
|
||||
// `type: secret` (R-254) it is fetched on demand (/stacks/<n>/auto-field/reveal).
|
||||
for _, f := range meta.DeployFields {
|
||||
if f.Type == "password" {
|
||||
delete(decryptedEnv, f.EnvVar)
|
||||
}
|
||||
}
|
||||
data["DeployedFieldValues"] = decryptedEnv
|
||||
data["RestoredLogins"] = restored
|
||||
}
|
||||
@@ -762,7 +770,10 @@ func (s *Server) appDetailHandler(w http.ResponseWriter, r *http.Request, slug s
|
||||
data["AppInfo"] = found.Meta.AppInfo
|
||||
// v0.279.0 (decision 45): the default-login card only while that login is in effect.
|
||||
data["KnownLoginLine"] = s.knownLoginLine(s.langFor(r), &found.Meta, found.AppConfig, found.Deployed)
|
||||
data["DefaultLoginReplaced"] = found.Meta.AfterInstall != nil && !defaultLoginInEffect(&found.Meta, found.AppConfig, found.Deployed)
|
||||
data["DefaultLoginReplaced"] = defaultLoginReplaced(&found.Meta, found.AppConfig, found.Deployed)
|
||||
// v0.280.0 (decision 46): the setup gate's card, while the gate stands.
|
||||
data["SetupGateClosed"] = found.Deployed && found.AppConfig != nil && found.AppConfig.SetupGate.Closed()
|
||||
data["SetupGateHasProbe"] = found.Meta.SetupDoneProbe != nil && found.Meta.SetupDoneProbe.URL != ""
|
||||
data["HasAppInfo"] = found.Meta.HasAppInfo()
|
||||
data["EffectiveSubdomain"] = effectiveSubdomain
|
||||
|
||||
@@ -1129,8 +1140,8 @@ func (s *Server) backupsRemoteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// 1./2./3. tier rows.
|
||||
func (s *Server) backupsAppsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := s.backupsCommonData("backups-apps", "Biztonsági mentés — Alkalmazások", r)
|
||||
data["TitleKey"] = "page.title.backups_apps" // i18n: the Hungarian title above is what hu renders
|
||||
s.backupsOffboxData(data, s.langFor(r)) // the tier-3 rows render $.Offbox status
|
||||
data["TitleKey"] = "page.title.backups_apps" // i18n: the Hungarian title above is what hu renders
|
||||
s.backupsOffboxData(data, s.langFor(r)) // the tier-3 rows render $.Offbox status
|
||||
data["HollowCopyLines"] = s.hollowCopyLines(s.langFor(r)) // Part D: running apps whose copy holds no data
|
||||
|
||||
if fullStatus, ok := data["Backup"].(*backup.FullBackupStatus); ok && fullStatus != nil {
|
||||
@@ -2477,7 +2488,9 @@ func (s *Server) appAutoFieldRevealHandler(w http.ResponseWriter, r *http.Reques
|
||||
escrowJSON(w, http.StatusBadRequest, nil, s.msg(r, "escrow.missing_field"))
|
||||
return
|
||||
}
|
||||
// AUTHORISATION: the field must be an auto-generated SECRET of this stack's catalog metadata.
|
||||
// AUTHORISATION: the field must be an auto-generated SECRET of this stack's catalog metadata — or, since
|
||||
// v0.280.0 (R-709), a `type: password` field of an INSTALLED app, unless a restore generated it (R-694:
|
||||
// that value is not the app's login and is never shown).
|
||||
allowed := false
|
||||
for _, f := range stack.Meta.AutoGeneratedFields() {
|
||||
if f.EnvVar == envVar && f.Type == "secret" {
|
||||
@@ -2485,6 +2498,18 @@ func (s *Server) appAutoFieldRevealHandler(w http.ResponseWriter, r *http.Reques
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allowed && stack.Deployed {
|
||||
restored := false
|
||||
if stack.AppConfig != nil {
|
||||
restored = slices.Contains(stack.AppConfig.RestoredLogins, envVar)
|
||||
}
|
||||
for _, f := range stack.Meta.UserFacingFields() {
|
||||
if f.EnvVar == envVar && f.Type == "password" && !restored {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !allowed {
|
||||
s.logger.Printf("[WARN] [web] auto-field reveal refused for %s/%s: not an auto-generated secret field", stackName, envVar)
|
||||
escrowJSON(w, http.StatusForbidden, nil, s.msg(r, "escrow.field_not_revealable"))
|
||||
|
||||
Reference in New Issue
Block a user