fix: validate attendance checkin payload

This commit is contained in:
richjohnrambo
2026-09-15 13:19:05 +08:00
parent bdb704832f
commit 1d994c9302
+72 -10
View File
@@ -248,31 +248,65 @@ func (a *API) attendanceCheckins(w http.ResponseWriter, r *http.Request, p *auth
return
}
var body struct {
Date string `json:"date"`
Action string `json:"action"`
RuleID string `json:"rule_id"`
ShiftID string `json:"shift_id"`
ClientTime string `json:"client_time"`
Location string `json:"location"`
Wifi string `json:"wifi"`
Device string `json:"device"`
Date string `json:"date"`
Action string `json:"action"`
RuleID string `json:"rule_id"`
ShiftID string `json:"shift_id"`
ClientTime string `json:"client_time"`
Location json.RawMessage `json:"location"`
Wifi json.RawMessage `json:"wifi"`
Device json.RawMessage `json:"device"`
}
if !decodeBody(w, r, &body) {
return
}
action := strings.TrimSpace(body.Action)
if action == "" || len(action) > 32 {
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "action"})
return
}
ruleID := strings.TrimSpace(body.RuleID)
if len(ruleID) > 64 {
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "rule_id"})
return
}
shiftID := strings.TrimSpace(body.ShiftID)
if shiftID == "" {
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "shift_id"})
return
}
date, err := parseDate(body.Date)
if err != nil {
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "date"})
return
}
if maxShiftIDLength := maxAttendanceShiftIDLength(p.UserID, date); len(shiftID) > maxShiftIDLength {
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "shift_id", "max_length": maxShiftIDLength})
return
}
clientTime, err := parseTime(body.ClientTime)
if err != nil {
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "client_time"})
return
}
location, err := normalizeJSONObject(body.Location)
if err != nil {
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "location"})
return
}
wifi, err := normalizeJSONObject(body.Wifi)
if err != nil {
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "wifi"})
return
}
device, err := normalizeJSONObject(body.Device)
if err != nil {
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "device"})
return
}
record, checkin, err := a.store.CreateAttendanceCheckin(r.Context(), p.UserID, "attc_"+randomToken(12), store.AttendanceCheckinRequest{
Date: date, Action: strings.TrimSpace(body.Action), RuleID: strings.TrimSpace(body.RuleID), ShiftID: strings.TrimSpace(body.ShiftID),
ClientTime: clientTime, Location: strings.TrimSpace(body.Location), Wifi: strings.TrimSpace(body.Wifi), Device: strings.TrimSpace(body.Device), IdempotencyKey: idempotencyKey,
Date: date, Action: action, RuleID: ruleID, ShiftID: shiftID,
ClientTime: clientTime, Location: location, Wifi: wifi, Device: device, IdempotencyKey: idempotencyKey,
})
if err != nil {
writeStoreError(w, r, err)
@@ -957,6 +991,34 @@ func parseTime(value string) (time.Time, error) {
return time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
}
func maxAttendanceShiftIDLength(userID int64, date time.Time) int {
prefix := fmt.Sprintf("att_%d_%s_", userID, date.Format("20060102"))
return 64 - len(prefix)
}
func normalizeJSONObject(raw json.RawMessage) (string, error) {
if len(raw) == 0 || strings.TrimSpace(string(raw)) == "" || string(raw) == "null" {
return "{}", nil
}
var text string
if err := json.Unmarshal(raw, &text); err == nil {
text = strings.TrimSpace(text)
if text == "" || text == "null" {
return "{}", nil
}
raw = json.RawMessage(text)
}
var value map[string]any
if err := json.Unmarshal(raw, &value); err != nil {
return "", err
}
normalized, err := json.Marshal(value)
if err != nil {
return "", err
}
return string(normalized), nil
}
func randomToken(size int) string {
buf := make([]byte, size)
if _, err := rand.Read(buf); err != nil {