fix: 独立审计日志并补齐令牌身份与失败记录
This commit is contained in:
+56
-45
@@ -225,7 +225,7 @@ func (h *Handler) handleEndpointCreate(w http.ResponseWriter, r *http.Request) {
|
||||
DefaultDelaySeconds *int64 `json:"default_delay_seconds"`
|
||||
}
|
||||
if err := httpx.DecodeJSON(r, &req); err != nil {
|
||||
h.audit(actorString(p), "endpoint_create", "", "bad_request", ip)
|
||||
h.auditP(p, "endpoint_create", "", "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "请求体无效")
|
||||
return
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func (h *Handler) handleEndpointCreate(w http.ResponseWriter, r *http.Request) {
|
||||
delaySec = *req.DefaultDelaySeconds
|
||||
}
|
||||
if errMsg := validateEndpointFields(req.ID, req.Name, req.Remark, req.LoginPassword, req.TalkPassword, delaySec, h.maxScheduleSeconds()); errMsg != "" {
|
||||
h.audit(actorString(p), "endpoint_create", req.ID, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_create", req.ID, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", errMsg)
|
||||
return
|
||||
}
|
||||
@@ -245,7 +245,7 @@ func (h *Handler) handleEndpointCreate(w http.ResponseWriter, r *http.Request) {
|
||||
if loginPW == "" {
|
||||
pw, err := generateLoginPassword()
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_create", id, "error", ip)
|
||||
h.auditP(p, "endpoint_create", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
@@ -254,7 +254,7 @@ func (h *Handler) handleEndpointCreate(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
loginHash, err := h.hash.Hash(r.Context(), auth.PasswordLogin, loginPW)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_create", id, "error", ip)
|
||||
h.auditP(p, "endpoint_create", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
@@ -262,7 +262,7 @@ func (h *Handler) handleEndpointCreate(w http.ResponseWriter, r *http.Request) {
|
||||
if req.TalkPassword != "" {
|
||||
th, hashErr := h.hash.Hash(r.Context(), auth.PasswordTalk, req.TalkPassword)
|
||||
if hashErr != nil {
|
||||
h.audit(actorString(p), "endpoint_create", id, "error", ip)
|
||||
h.auditP(p, "endpoint_create", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
@@ -280,15 +280,15 @@ func (h *Handler) handleEndpointCreate(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
if err != nil {
|
||||
if isUniqueConstraint(err) {
|
||||
h.audit(actorString(p), "endpoint_create", id, "id_taken", ip)
|
||||
h.auditP(p, "endpoint_create", id, "id_taken", ip)
|
||||
httpx.WriteError(w, http.StatusConflict, "id_taken", "编号已占用")
|
||||
return
|
||||
}
|
||||
h.audit(actorString(p), "endpoint_create", id, "error", ip)
|
||||
h.auditP(p, "endpoint_create", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
h.audit(actorString(p), "endpoint_create", createdID, "ok", ip)
|
||||
h.auditP(p, "endpoint_create", createdID, "ok", ip)
|
||||
data := map[string]any{"id": createdID}
|
||||
if pwGenerated {
|
||||
data[loginPasswordOnceKey] = loginPW
|
||||
@@ -322,28 +322,28 @@ func (h *Handler) handleEndpointPatch(w http.ResponseWriter, r *http.Request) {
|
||||
Enabled *bool `json:"enabled"`
|
||||
}
|
||||
if err := httpx.DecodeJSON(r, &req); err != nil {
|
||||
h.audit(actorString(p), "endpoint_patch", id, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "请求体无效")
|
||||
return
|
||||
}
|
||||
if req.Name == nil && req.Remark == nil && req.DefaultDelaySeconds == nil && req.Enabled == nil {
|
||||
h.audit(actorString(p), "endpoint_patch", id, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "无更新字段")
|
||||
return
|
||||
}
|
||||
if req.Name != nil && !protocol.ValidName(*req.Name) {
|
||||
h.audit(actorString(p), "endpoint_patch", id, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "名称不合法")
|
||||
return
|
||||
}
|
||||
if req.Remark != nil && utf8.RuneCountInString(*req.Remark) > maxRemarkChars {
|
||||
h.audit(actorString(p), "endpoint_patch", id, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "备注过长")
|
||||
return
|
||||
}
|
||||
if req.DefaultDelaySeconds != nil {
|
||||
if msg := validateDelaySeconds(*req.DefaultDelaySeconds, h.maxScheduleSeconds()); msg != "" {
|
||||
h.audit(actorString(p), "endpoint_patch", id, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", msg)
|
||||
return
|
||||
}
|
||||
@@ -361,11 +361,11 @@ func (h *Handler) handleEndpointPatch(w http.ResponseWriter, r *http.Request) {
|
||||
wasEnabled, err = h.patchEndpoint(r.Context(), id, req.Name, req.Remark, req.DefaultDelaySeconds, patchEnabled)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
h.audit(actorString(p), "endpoint_patch", id, "not_found", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "not_found", ip)
|
||||
httpx.WriteError(w, http.StatusNotFound, "not_found", "端不存在")
|
||||
return
|
||||
}
|
||||
h.audit(actorString(p), "endpoint_patch", id, "error", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
@@ -374,12 +374,12 @@ func (h *Handler) handleEndpointPatch(w http.ResponseWriter, r *http.Request) {
|
||||
var found bool
|
||||
found, err = h.setEndpointEnabled(r.Context(), id, *req.Enabled)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_patch", id, "error", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
if !found {
|
||||
h.audit(actorString(p), "endpoint_patch", id, "not_found", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "not_found", ip)
|
||||
httpx.WriteError(w, http.StatusNotFound, "not_found", "端不存在")
|
||||
return
|
||||
}
|
||||
@@ -391,11 +391,19 @@ func (h *Handler) handleEndpointPatch(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
row, err := h.getEndpoint(r.Context(), id)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_patch", id, "error", ip)
|
||||
h.auditP(p, "endpoint_patch", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
h.audit(actorString(p), "endpoint_patch", id, "ok", ip)
|
||||
detail := map[string]any{}
|
||||
if req.Enabled != nil {
|
||||
detail["enabled"] = *req.Enabled
|
||||
}
|
||||
if len(detail) == 0 {
|
||||
h.auditP(p, "endpoint_patch", id, "ok", ip)
|
||||
} else {
|
||||
h.auditPD(p, "endpoint_patch", id, "ok", ip, detail)
|
||||
}
|
||||
httpx.WriteOK(w, row.toAPI(h.endpointLoginLocked(row.ID), true))
|
||||
}
|
||||
|
||||
@@ -406,17 +414,17 @@ func (h *Handler) handleEndpointDelete(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ok, err := h.deleteEndpointBasic(r.Context(), id)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_delete", id, "error", ip)
|
||||
h.auditP(p, "endpoint_delete", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
h.audit(actorString(p), "endpoint_delete", id, "not_found", ip)
|
||||
h.auditP(p, "endpoint_delete", id, "not_found", ip)
|
||||
httpx.WriteError(w, http.StatusNotFound, "not_found", "端不存在")
|
||||
return
|
||||
}
|
||||
h.afterDeleteKick(r.Context(), id)
|
||||
h.audit(actorString(p), "endpoint_delete", id, "ok", ip)
|
||||
h.auditP(p, "endpoint_delete", id, "ok", ip)
|
||||
httpx.WriteOK(w, map[string]any{})
|
||||
}
|
||||
|
||||
@@ -429,14 +437,14 @@ func (h *Handler) handleEndpointBatch(w http.ResponseWriter, r *http.Request) {
|
||||
Action string `json:"action"`
|
||||
}
|
||||
if err := httpx.DecodeJSON(r, &req); err != nil || len(req.IDs) == 0 {
|
||||
h.audit(actorString(p), "endpoint_batch", "", "bad_request", ip)
|
||||
h.auditP(p, "endpoint_batch", "", "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "请求体无效")
|
||||
return
|
||||
}
|
||||
switch req.Action {
|
||||
case "disable", "enable", "delete":
|
||||
default:
|
||||
h.audit(actorString(p), "endpoint_batch", "", "bad_request", ip)
|
||||
h.auditP(p, "endpoint_batch", "", "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "action 无效")
|
||||
return
|
||||
}
|
||||
@@ -470,7 +478,10 @@ func (h *Handler) handleEndpointBatch(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
okIDs = append(okIDs, id)
|
||||
}
|
||||
h.audit(actorString(p), "endpoint_batch_"+req.Action, strings.Join(okIDs, ","), "ok", ip)
|
||||
h.auditPD(p, "endpoint_batch_"+req.Action, strings.Join(okIDs, ","), batchAuditResult(len(okIDs), len(failed)), ip, map[string]any{
|
||||
"ok_ids": okIDs,
|
||||
"failed": failed,
|
||||
})
|
||||
httpx.WriteOK(w, map[string]any{"ok_ids": okIDs, "failed": failed})
|
||||
}
|
||||
|
||||
@@ -481,22 +492,22 @@ func (h *Handler) handleEndpointKick(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
exists, err := h.endpointExists(r.Context(), id)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_kick", id, "error", ip)
|
||||
h.auditP(p, "endpoint_kick", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
h.audit(actorString(p), "endpoint_kick", id, "not_found", ip)
|
||||
h.auditP(p, "endpoint_kick", id, "not_found", ip)
|
||||
httpx.WriteError(w, http.StatusNotFound, "not_found", "端不存在")
|
||||
return
|
||||
}
|
||||
kicked, err := h.kickEndpoint(r.Context(), id)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_kick", id, "error", ip)
|
||||
h.auditP(p, "endpoint_kick", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
h.audit(actorString(p), "endpoint_kick", id, "ok", ip)
|
||||
h.auditP(p, "endpoint_kick", id, "ok", ip)
|
||||
httpx.WriteOK(w, map[string]any{"kicked": kicked})
|
||||
}
|
||||
|
||||
@@ -509,12 +520,12 @@ func (h *Handler) handleEndpointResetLoginPassword(w http.ResponseWriter, r *htt
|
||||
LoginPassword string `json:"login_password"`
|
||||
}
|
||||
if err := httpx.DecodeJSON(r, &req); err != nil {
|
||||
h.audit(actorString(p), "endpoint_reset_login_password", id, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_reset_login_password", id, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "请求体无效")
|
||||
return
|
||||
}
|
||||
if !protocol.ValidLoginPassword(req.LoginPassword) {
|
||||
h.audit(actorString(p), "endpoint_reset_login_password", id, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_reset_login_password", id, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "登录密码不合法")
|
||||
return
|
||||
}
|
||||
@@ -523,7 +534,7 @@ func (h *Handler) handleEndpointResetLoginPassword(w http.ResponseWriter, r *htt
|
||||
if pw == "" {
|
||||
gen, err := generateLoginPassword()
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_reset_login_password", id, "error", ip)
|
||||
h.auditP(p, "endpoint_reset_login_password", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
@@ -531,23 +542,23 @@ func (h *Handler) handleEndpointResetLoginPassword(w http.ResponseWriter, r *htt
|
||||
}
|
||||
hash, err := h.hash.Hash(r.Context(), auth.PasswordLogin, pw)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_reset_login_password", id, "error", ip)
|
||||
h.auditP(p, "endpoint_reset_login_password", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
ok, err := h.resetLoginPassword(r.Context(), id, hash)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_reset_login_password", id, "error", ip)
|
||||
h.auditP(p, "endpoint_reset_login_password", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
h.audit(actorString(p), "endpoint_reset_login_password", id, "not_found", ip)
|
||||
h.auditP(p, "endpoint_reset_login_password", id, "not_found", ip)
|
||||
httpx.WriteError(w, http.StatusNotFound, "not_found", "端不存在")
|
||||
return
|
||||
}
|
||||
_, _ = h.passwordResetKick(r.Context(), id)
|
||||
h.audit(actorString(p), "endpoint_reset_login_password", id, "ok", ip)
|
||||
h.auditP(p, "endpoint_reset_login_password", id, "ok", ip)
|
||||
httpx.WriteOK(w, map[string]any{loginPasswordOnceKey: pw})
|
||||
}
|
||||
|
||||
@@ -560,12 +571,12 @@ func (h *Handler) handleEndpointTalkPassword(w http.ResponseWriter, r *http.Requ
|
||||
TalkPassword string `json:"talk_password"`
|
||||
}
|
||||
if err := httpx.DecodeJSON(r, &req); err != nil {
|
||||
h.audit(actorString(p), "endpoint_talk_password", id, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_talk_password", id, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "请求体无效")
|
||||
return
|
||||
}
|
||||
if !protocol.ValidTalkPassword(req.TalkPassword) {
|
||||
h.audit(actorString(p), "endpoint_talk_password", id, "bad_request", ip)
|
||||
h.auditP(p, "endpoint_talk_password", id, "bad_request", ip)
|
||||
httpx.WriteError(w, http.StatusBadRequest, "bad_request", "对话密码不合法")
|
||||
return
|
||||
}
|
||||
@@ -574,7 +585,7 @@ func (h *Handler) handleEndpointTalkPassword(w http.ResponseWriter, r *http.Requ
|
||||
if req.TalkPassword != "" {
|
||||
th, err := h.hash.Hash(r.Context(), auth.PasswordTalk, req.TalkPassword)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_talk_password", id, "error", ip)
|
||||
h.auditP(p, "endpoint_talk_password", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
@@ -582,16 +593,16 @@ func (h *Handler) handleEndpointTalkPassword(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
ok, err := h.setTalkPassword(r.Context(), id, talkHash)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_talk_password", id, "error", ip)
|
||||
h.auditP(p, "endpoint_talk_password", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
h.audit(actorString(p), "endpoint_talk_password", id, "not_found", ip)
|
||||
h.auditP(p, "endpoint_talk_password", id, "not_found", ip)
|
||||
httpx.WriteError(w, http.StatusNotFound, "not_found", "端不存在")
|
||||
return
|
||||
}
|
||||
h.audit(actorString(p), "endpoint_talk_password", id, "ok", ip)
|
||||
h.auditP(p, "endpoint_talk_password", id, "ok", ip)
|
||||
httpx.WriteOK(w, map[string]any{"talk_password_set": talkHash.Valid})
|
||||
}
|
||||
|
||||
@@ -602,17 +613,17 @@ func (h *Handler) handleEndpointUnlock(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
exists, err := h.endpointExists(r.Context(), id)
|
||||
if err != nil {
|
||||
h.audit(actorString(p), "endpoint_unlock", id, "error", ip)
|
||||
h.auditP(p, "endpoint_unlock", id, "error", ip)
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
h.audit(actorString(p), "endpoint_unlock", id, "not_found", ip)
|
||||
h.auditP(p, "endpoint_unlock", id, "not_found", ip)
|
||||
httpx.WriteError(w, http.StatusNotFound, "not_found", "端不存在")
|
||||
return
|
||||
}
|
||||
h.locks.ClearEndpoint(id)
|
||||
h.audit(actorString(p), "endpoint_unlock", id, "ok", ip)
|
||||
h.auditP(p, "endpoint_unlock", id, "ok", ip)
|
||||
httpx.WriteOK(w, map[string]any{})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user