fix(server): harden per-org settings migration and sweeps

Review follow-ups on e5363a6:

- MigrateSettingsOrg no longer guesses via the "default" slug. One org
  means stamp that org; zero orgs means synthesise Default; more than
  one means leave it alone and log, since guessing would hand one org
  another's SMTP config and ESO token.
- EnsureSettingsIndexes failure is now fatal. Without the unique index
  on org_id, GetSettings returns an arbitrary duplicate; without the one
  on the token hash, ResolveSecretsReadToken picks an arbitrary org.
- Name the token-hash index explicitly so it stops colliding with the
  legacy name DropOne targets, and exclude the empty string from the
  partial filter.
- Log retention: distinguish a missing run doc from a Mongo error, so a
  transient failure skips the directory rather than purging it at the
  30-day default.
- Offline sweep: fresh context per org, log-and-continue on a per-org
  error, plus a final pass for servers whose org no longer exists.
- ESO handler 401s on an empty token-derived org rather than querying
  org_id "".
This commit is contained in:
2026-07-22 09:41:51 +01:00
parent e5363a64ee
commit 4f512d01f1
6 changed files with 132 additions and 63 deletions
+5
View File
@@ -53,6 +53,11 @@ func esoGetGroup(c *gin.Context) {
group := c.Param("group")
// Org comes from the bearer token (set by secretsReadAuth), not a session.
orgID := c.GetString(ctxSecretsOrgKey)
if orgID == "" {
// Defence in depth: never query the store unscoped.
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
return
}
values, err := services.GetSecretGroupDecrypted(orgID, group)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "store error"})