feat: 新增共库模式下登录失败日志表及相关功能
- 新增登录失败日志表(共库多租户版),添加 tenant_id 列实现租户隔离 - 调整登录失败日志逻辑,仅记录触发账号锁定的事件 - 新增管理端接口功能,为登录失败日志补充实时锁定状态 - 更新测试用例适配共库模式下的租户过滤逻辑
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tinode/chat/server/store"
|
||||
"github.com/tinode/chat/server/store/types"
|
||||
@@ -156,6 +157,9 @@ func serveInternalAdminResourceList(wrt http.ResponseWriter, req *http.Request,
|
||||
return
|
||||
}
|
||||
}
|
||||
if resource == "login-failure-logs" {
|
||||
attachLoginLockoutState(tenantID, result.Items)
|
||||
}
|
||||
writeIMSuccess(wrt, req, map[string]any{
|
||||
"items": normalizeInternalAdminRowsForAPI(result.Items),
|
||||
"total": result.Total,
|
||||
@@ -637,6 +641,40 @@ func normalizeInternalAdminRowForAPI(row map[string]any) map[string]any {
|
||||
return row
|
||||
}
|
||||
|
||||
// internalAdminRowString 把 DB 行字段安全转为字符串(driver 可能返回 string 或 []byte)。
|
||||
func internalAdminRowString(row map[string]any, key string) string {
|
||||
switch v := row[key].(type) {
|
||||
case string:
|
||||
return strings.TrimSpace(v)
|
||||
case []byte:
|
||||
return strings.TrimSpace(string(v))
|
||||
default:
|
||||
return strings.TrimSpace(fmt.Sprint(v))
|
||||
}
|
||||
}
|
||||
|
||||
// attachLoginLockoutState 为登录失败日志的锁定行补充 Redis 实时锁定状态。
|
||||
// 锁定记录存于 Redis(im_login_lockouts 表已废弃写入),日志行是历史快照不会更新,
|
||||
// 管理端解锁按钮依赖 locked_now 判断"当前是否仍锁定中",解锁成功后按钮随刷新消失。
|
||||
func attachLoginLockoutState(tenantID types.TenantID, items []map[string]any) {
|
||||
now := time.Now()
|
||||
for _, item := range items {
|
||||
if item == nil || !strings.EqualFold(internalAdminRowString(item, "risk_action"), loginAuditRiskActionLocked) {
|
||||
continue
|
||||
}
|
||||
accountHash := internalAdminRowString(item, "account_hash")
|
||||
if accountHash == "" {
|
||||
continue
|
||||
}
|
||||
locked := false
|
||||
if lockout, err := store.LoginLockouts.Get(tenantID, accountHash); err == nil && lockout != nil &&
|
||||
lockout.LockedUntil != nil && lockout.LockedUntil.After(now) {
|
||||
locked = true
|
||||
}
|
||||
item["locked_now"] = locked
|
||||
}
|
||||
}
|
||||
|
||||
func parseInternalAdminDBID(raw string) (int64, bool) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
|
||||
Reference in New Issue
Block a user