(c *gin.Context)
| 48 | } |
| 49 | |
| 50 | func (controller *UserController) loginHandler(c *gin.Context) { |
| 51 | var req LoginRequest |
| 52 | |
| 53 | err := c.ShouldBindJSON(&req) |
| 54 | if err != nil { |
| 55 | tlog.App.Error().Err(err).Msg("Failed to bind JSON") |
| 56 | c.JSON(400, gin.H{ |
| 57 | "status": 400, |
| 58 | "message": "Bad Request", |
| 59 | }) |
| 60 | return |
| 61 | } |
| 62 | |
| 63 | tlog.App.Debug().Str("username", req.Username).Msg("Login attempt") |
| 64 | |
| 65 | isLocked, remaining := controller.auth.IsAccountLocked(req.Username) |
| 66 | |
| 67 | if isLocked { |
| 68 | tlog.App.Warn().Str("username", req.Username).Msg("Account is locked due to too many failed login attempts") |
| 69 | tlog.AuditLoginFailure(c, req.Username, "username", "account locked") |
| 70 | c.Writer.Header().Add("x-tinyauth-lock-locked", "true") |
| 71 | c.Writer.Header().Add("x-tinyauth-lock-reset", time.Now().Add(time.Duration(remaining)*time.Second).Format(time.RFC3339)) |
| 72 | c.JSON(429, gin.H{ |
| 73 | "status": 429, |
| 74 | "message": fmt.Sprintf("Too many failed login attempts. Try again in %d seconds", remaining), |
| 75 | }) |
| 76 | return |
| 77 | } |
| 78 | |
| 79 | userSearch := controller.auth.SearchUser(req.Username) |
| 80 | |
| 81 | if userSearch.Type == "unknown" { |
| 82 | tlog.App.Warn().Str("username", req.Username).Msg("User not found") |
| 83 | controller.auth.RecordLoginAttempt(req.Username, false) |
| 84 | tlog.AuditLoginFailure(c, req.Username, "username", "user not found") |
| 85 | c.JSON(401, gin.H{ |
| 86 | "status": 401, |
| 87 | "message": "Unauthorized", |
| 88 | }) |
| 89 | return |
| 90 | } |
| 91 | |
| 92 | if !controller.auth.VerifyUser(userSearch, req.Password) { |
| 93 | tlog.App.Warn().Str("username", req.Username).Msg("Invalid password") |
| 94 | controller.auth.RecordLoginAttempt(req.Username, false) |
| 95 | tlog.AuditLoginFailure(c, req.Username, "username", "invalid password") |
| 96 | c.JSON(401, gin.H{ |
| 97 | "status": 401, |
| 98 | "message": "Unauthorized", |
| 99 | }) |
| 100 | return |
| 101 | } |
| 102 | |
| 103 | tlog.App.Info().Str("username", req.Username).Msg("Login successful") |
| 104 | tlog.AuditLoginSuccess(c, req.Username, "username") |
| 105 | |
| 106 | controller.auth.RecordLoginAttempt(req.Username, true) |
| 107 |
nothing calls this directly
no test coverage detected