feat: 接入后台消息治理与登录审计

This commit is contained in:
richjohnrambo
2026-09-07 11:53:24 +08:00
parent 90185b6c88
commit 6b20a633ce
17 changed files with 1062 additions and 45 deletions
+43 -10
View File
@@ -461,6 +461,9 @@ func (s *Session) purgeChannels() {
func (s *Session) cleanUp(expired bool) {
atomic.StoreInt32(&s.terminating, 1)
if !s.uid.IsZero() && s.tenantID.IsValid() {
if err := store.SecurityAudit.CloseLogin(s.tenantID, s.sid, time.Now().UTC().Round(time.Millisecond)); err != nil {
logs.Warn.Println("s.cleanUp: failed to close login audit", s.uid, err, s.sid)
}
if err := store.MarkUserOffline(s.tenantID, s.uid, s.sid, currentNodeID()); err != nil {
logs.Warn.Println("s.cleanUp: failed to mark user offline", s.uid, err, s.sid)
}
@@ -498,24 +501,46 @@ func (s *Session) dispatchRaw(raw []byte) {
return
}
toLog := raw
truncated := ""
if len(raw) > 512 {
toLog = raw[:512]
truncated = "<...>"
}
logs.Info.Printf("in: '%s%s' sid='%s' uid='%s'", toLog, truncated, s.sid, s.uid)
if err := json.Unmarshal(raw, &msg); err != nil {
// Malformed message
logs.Warn.Println("s.dispatch", err, s.sid)
s.queueOut(ErrMalformed("", "", now))
return
}
toLog := redactClientMessageForLog(raw)
truncated := ""
if len(toLog) > 512 {
toLog = toLog[:512]
truncated = "<...>"
}
logs.Info.Printf("in: '%s%s' sid='%s' uid='%s'", toLog, truncated, s.sid, s.uid)
s.dispatch(&msg)
}
func redactClientMessageForLog(raw []byte) []byte {
var payload map[string]any
if json.Unmarshal(raw, &payload) != nil {
return []byte("[malformed payload]")
}
for _, section := range []string{"login", "acc"} {
body, ok := payload[section].(map[string]any)
if !ok {
continue
}
for _, field := range []string{"secret", "tmpsecret"} {
if _, exists := body[field]; exists {
body[field] = "[REDACTED]"
}
}
}
redacted, err := json.Marshal(payload)
if err != nil {
return []byte("[unprintable payload]")
}
return redacted
}
func (s *Session) dispatch(msg *ClientComMessage) {
now := types.TimeNow()
atomic.StoreInt64(&s.lastAction, now.UnixNano())
@@ -1089,6 +1114,7 @@ func (s *Session) login(msg *ClientComMessage) {
handler := store.Store.GetLogicalAuthHandler(msg.Login.Scheme)
if handler == nil {
recordSessionLoginFailure(s, msg.Login.Scheme, msg.Login.Secret, nil, msg.Id, types.ErrUnsupported, msg.Timestamp)
logs.Warn.Println("s.login: unknown authentication scheme", msg.Login.Scheme, s.sid)
s.queueOut(ErrAuthUnknownScheme(msg.Id, "", msg.Timestamp))
return
@@ -1097,6 +1123,7 @@ func (s *Session) login(msg *ClientComMessage) {
rec, challenge, err := handler.Authenticate(
auth.AuthContext{TenantID: s.tenantID, RemoteAddr: s.remoteAddr}, msg.Login.Secret)
if err != nil {
recordSessionLoginFailure(s, msg.Login.Scheme, msg.Login.Secret, rec, msg.Id, err, msg.Timestamp)
resp := decodeStoreError(err, msg.Id, msg.Timestamp, nil)
if resp.Ctrl.Code >= 500 {
// Log internal errors
@@ -1107,6 +1134,7 @@ func (s *Session) login(msg *ClientComMessage) {
}
if rec == nil || rec.TenantID != s.tenantID {
recordSessionLoginFailure(s, msg.Login.Scheme, msg.Login.Secret, rec, msg.Id, types.ErrFailed, msg.Timestamp)
logs.Warn.Println("s.login: authenticator returned invalid tenant", s.sid)
s.queueOut(decodeStoreError(types.ErrFailed, msg.Id, msg.Timestamp, nil))
return
@@ -1121,6 +1149,7 @@ func (s *Session) login(msg *ClientComMessage) {
}
if err != nil {
recordSessionLoginFailure(s, msg.Login.Scheme, msg.Login.Secret, rec, msg.Id, err, msg.Timestamp)
logs.Warn.Println("s.login: user state check failed", rec.Uid, err, s.sid)
s.queueOut(decodeStoreError(err, msg.Id, msg.Timestamp, nil))
return
@@ -1142,10 +1171,11 @@ func (s *Session) login(msg *ClientComMessage) {
}
}
if err != nil {
recordSessionLoginFailure(s, msg.Login.Scheme, msg.Login.Secret, rec, msg.Id, err, msg.Timestamp)
logs.Warn.Println("s.login: failed to validate credentials:", err, s.sid)
s.queueOut(decodeStoreError(err, msg.Id, msg.Timestamp, nil))
} else {
s.queueOut(s.onLogin(msg.Id, msg.Timestamp, rec, missing))
s.queueOut(s.onLogin(msg.Id, msg.Timestamp, rec, missing, msg.Login.Scheme, strings.SplitN(string(msg.Login.Secret), ":", 2)[0]))
}
}
@@ -1209,7 +1239,7 @@ func (s *Session) authSecretReset(params []byte) error {
}
// onLogin performs steps after successful authentication.
func (s *Session) onLogin(msgID string, timestamp time.Time, rec *auth.Rec, missing []string) *ServerComMessage {
func (s *Session) onLogin(msgID string, timestamp time.Time, rec *auth.Rec, missing []string, scheme, account string) *ServerComMessage {
var reply *ServerComMessage
var params map[string]any
@@ -1266,6 +1296,9 @@ func (s *Session) onLogin(msgID string, timestamp time.Time, rec *auth.Rec, miss
logs.Warn.Println("failed to update device record", err)
}
}
if !s.uid.IsZero() {
recordSessionLoginSuccess(s, rec, scheme, account, msgID, timestamp)
}
}
// GenSecret fails only if tokenLifetime is < 0. It can't be < 0 here,