fix: repair migration collection names and cross-cutting scoping gaps
Findings from the final whole-branch review. - scopedCollections named "audit" and "channels", but the code writes to audit_logs and notification_channels. On upgrade from single-tenant, legacy audit events and channels would never get org_id, becoming invisible to org-filtered reads while channels silently stopped firing — and the detection loop counted the wrong names, so the 0001 marker could be written having migrated nothing. Names fixed, plus migration 0003 so an incorrectly-migrated instance converges with a fresh one. - EnsureAuthIndexes failure is now fatal. GetUserByEmail is unscoped and the OIDC cross-org guard compares against whichever duplicate Mongo returns first, so users.email uniqueness is a security invariant, and a legacy collection with duplicate emails is the realistic upgrade case. - Evict the per-org OIDC provider cache on save; rotating away from a compromised IdP previously had no effect until restart. - Build the oauth2 config per request instead of mutating a shared cached pointer outside the mutex, which raced on RedirectURL between concurrent logins for the same org. - Stamp org_id on console_sessions, incidents and monitor_rollups, the last collections with no tenant column. 0003 derives their org from the owning server/monitor rather than defaulting, so one org's console history and incident timeline cannot merge into another's. - Seed default steps when an org is created, not only at boot. - Reject an empty session OrgID at the middleware. - Derive the app root label from APP_ROOT_LABEL instead of hardcoding "vantage", which silently disabled the host guard off that domain. - Stop caching negative slug lookups, so a new org's subdomain resolves immediately.
This commit is contained in:
@@ -36,7 +36,7 @@ func consoleConnect(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
sess, err := services.CreateConsoleSession(body.ServerID, body.Protocol, body.KeyID, actorFromCtx(c), c.ClientIP())
|
||||
sess, err := services.CreateConsoleSession(auth.OrgID(c), body.ServerID, body.Protocol, body.KeyID, actorFromCtx(c), c.ClientIP())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
@@ -48,14 +48,14 @@ func consoleConnect(c *gin.Context) {
|
||||
}
|
||||
|
||||
if (body.Protocol == "rdp" || body.Protocol == "vnc") && (body.RDPUsername != "" || body.RDPPassword != "") {
|
||||
if err := services.StashConsoleRDPCreds(sess.SessionID, body.RDPUsername, body.RDPPassword); err != nil {
|
||||
if err := services.StashConsoleRDPCreds(auth.OrgID(c), sess.SessionID, body.RDPUsername, body.RDPPassword); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if body.Protocol == "ssh" {
|
||||
if err := services.SetConsoleSSHUser(sess.SessionID, body.SSHUsername); err != nil {
|
||||
if err := services.SetConsoleSSHUser(auth.OrgID(c), sess.SessionID, body.SSHUsername); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
@@ -89,7 +89,8 @@ func consoleTunnel(c *gin.Context) {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
|
||||
return
|
||||
}
|
||||
sess, err := services.GetConsoleSession(sessionID)
|
||||
orgID := auth.OrgID(c)
|
||||
sess, err := services.GetConsoleSession(orgID, sessionID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "session not found"})
|
||||
return
|
||||
@@ -103,7 +104,7 @@ func consoleTunnel(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Single-use: atomically spend the token so a replay within its TTL is rejected.
|
||||
if err := services.ConsumeSessionToken(sessionID); err != nil {
|
||||
if err := services.ConsumeSessionToken(orgID, sessionID); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "token already used"})
|
||||
return
|
||||
}
|
||||
@@ -127,7 +128,7 @@ func consoleTunnel(c *gin.Context) {
|
||||
|
||||
var rdpUser, rdpPass string
|
||||
if sess.Protocol == "rdp" || sess.Protocol == "vnc" {
|
||||
rdpUser, rdpPass, err = services.ConsumeConsoleRDPCreds(sessionID)
|
||||
rdpUser, rdpPass, err = services.ConsumeConsoleRDPCreds(orgID, sessionID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "could not load credentials"})
|
||||
return
|
||||
@@ -172,7 +173,7 @@ func consoleTunnel(c *gin.Context) {
|
||||
|
||||
wsServer := guac.NewWebsocketServer(connect)
|
||||
wsServer.OnDisconnect = func(id string, r *http.Request, t guac.Tunnel) {
|
||||
_ = services.EndConsoleSession(sessionID)
|
||||
_ = services.EndConsoleSession(orgID, sessionID)
|
||||
}
|
||||
wsServer.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func getMonitorIncidents(c *gin.Context) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "monitor not found"})
|
||||
return
|
||||
}
|
||||
incidents, err := services.ListIncidents(c.Param("id"), 50)
|
||||
incidents, err := services.ListIncidents(auth.OrgID(c), c.Param("id"), 50)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
@@ -149,7 +149,7 @@ func getMonitorUptime(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
since := time.Now().Add(-30 * 24 * time.Hour)
|
||||
rollups, err := services.UptimeRollups(c.Param("id"), since)
|
||||
rollups, err := services.UptimeRollups(auth.OrgID(c), c.Param("id"), since)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
|
||||
@@ -155,5 +155,8 @@ func putOrgOIDC(c *gin.Context) {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
// Drop the cached provider so a rotated issuer takes effect immediately —
|
||||
// an admin moving off a compromised IdP must not keep authenticating there.
|
||||
auth.EvictOIDCProvider(auth.OrgID(c))
|
||||
c.JSON(http.StatusOK, gin.H{"saved": true})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user