MFA fix, added IP blacklist, update webmail client

This commit is contained in:
2026-08-14 13:04:55 +01:00
parent 6063f95504
commit 892f366a16
122 changed files with 13362 additions and 251 deletions
+27
View File
@@ -132,6 +132,33 @@ func (d *DB) LogAuthAttempt(authType, identifier, ipAddress string, success bool
return err
}
// CountRecentFailedAttempts counts failed esrv_auth_logs rows for one identifier
// (independent of which IP each attempt came from — a distributed credential-
// stuffing attempt against a single account should still trip this) within
// authType and since the given cutoff, powering the per-account lockout in
// internal/webui/login.go and webmail_login.go.
func (d *DB) CountRecentFailedAttempts(authType, identifier string, since time.Time) (int, error) {
var n int
// created_at is populated by SQLite's own CURRENT_TIMESTAMP: a plain
// "YYYY-MM-DD HH:MM:SS" UTC string, space-separated, no fractional seconds, no
// offset. modernc.org/sqlite instead binds a Go time.Time query parameter as
// RFC3339Nano with a zone offset (e.g. "2026-08-14T06:57:50.497566315+01:00") —
// a live check confirmed this by inserting a time.Time into a real column and
// reading the stored text back. That format is structurally different from
// CURRENT_TIMESTAMP's own (different separator, precision, and offset), so a
// plain text >= comparison between the two doesn't reflect chronological order at
// all (confirmed: it silently matched zero rows). Two Go-bound time.Time values
// compared against each other DO work correctly, since the driver formats both
// identically — this only breaks when one side is a raw SQL CURRENT_TIMESTAMP
// default and the other is a Go-bound parameter, which happens on THIS column but
// nowhere else in this codebase (checked every other DATETIME comparison).
// Formatting since into CURRENT_TIMESTAMP's exact layout makes both sides match.
err := d.QueryRow(`SELECT COUNT(*) FROM esrv_auth_logs
WHERE auth_type = ? AND identifier = ? AND success = 0 AND created_at >= ?`,
authType, identifier, since.UTC().Format("2006-01-02 15:04:05")).Scan(&n)
return n, err
}
func parseTime(s string) (time.Time, error) {
for _, layout := range []string{"2006-01-02 15:04:05.999999999-07:00", "2006-01-02 15:04:05", time.RFC3339} {
if t, err := time.Parse(layout, s); err == nil {