Files
goaway/backend/api/auth.go
T

173 lines
4.5 KiB
Go

package api
import (
"context"
"encoding/json"
"fmt"
"goaway/backend/alert"
"goaway/backend/audit"
"goaway/backend/user"
"io"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func (api *API) registerAuthRoutes() {
api.router.POST("/api/login", api.handleLogin)
api.router.GET("/api/authentication", api.getAuthentication)
api.routes.PUT("/password", api.updatePassword)
api.routes.POST("/apiKey", api.createAPIKey)
api.routes.GET("/apiKey", api.getAPIKeys)
api.routes.GET("/deleteApiKey", api.deleteAPIKey)
}
func (api *API) handleLogin(c *gin.Context) {
allowed, timeUntilReset := api.RateLimiter.CheckLimit(c.ClientIP())
if !allowed {
c.JSON(http.StatusTooManyRequests, gin.H{
"error": "Too many login attempts. Please try again later.",
"retryAfterSeconds": timeUntilReset,
})
return
}
var loginUser user.User
if err := c.BindJSON(&loginUser); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
return
}
if err := api.UserService.ValidateCredentials(loginUser); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}
if api.UserService.Authenticate(loginUser.Username, loginUser.Password) {
token, err := generateToken(loginUser.Username, api.Config.API.JWTSecret)
if err != nil {
log.Info("Token generation failed for user %s: %v", loginUser.Username, err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Authentication service temporarily unavailable",
})
return
}
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Credentials", "true")
setAuthCookie(c.Writer, token)
c.JSON(http.StatusOK, gin.H{"message": "Login successful"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{
"error": "Invalid username or password",
})
}
}
func (api *API) getAuthentication(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"enabled": api.Authentication})
}
func (api *API) updatePassword(c *gin.Context) {
type passwordChange struct {
CurrentPassword string `json:"currentPassword"`
NewPassword string `json:"newPassword"`
}
var newCredentials passwordChange
if err := c.BindJSON(&newCredentials); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
return
}
if !api.UserService.Authenticate("admin", newCredentials.CurrentPassword) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Current password is not valid"})
return
}
if err := api.UserService.UpdatePassword("admin", newCredentials.NewPassword); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Unable to update password"})
return
}
logMsg := "Password changed for user 'admin'"
api.DNSServer.AuditService.CreateAudit(&audit.Entry{
Topic: audit.TopicUser,
Message: logMsg,
})
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = api.DNSServer.AlertService.SendToAll(ctx, alert.Message{
Title: "System",
Content: logMsg,
Severity: SeverityWarning,
})
}()
log.Warning("%s", logMsg)
c.Status(http.StatusOK)
}
func (api *API) createAPIKey(c *gin.Context) {
type NewAPIKeyName struct {
Name string `json:"name"`
}
body, err := io.ReadAll(c.Request.Body)
if err != nil {
log.Error("Failed to read request body: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
return
}
var request NewAPIKeyName
if err := json.Unmarshal(body, &request); err != nil {
log.Error("Failed to parse JSON: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON format"})
return
}
apiKey, err := api.KeyService.CreateKey(request.Name)
if err != nil {
c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
return
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = api.DNSServer.AlertService.SendToAll(ctx, alert.Message{
Title: "System",
Content: fmt.Sprintf("New API key created with the name '%s'", request.Name),
Severity: SeverityWarning,
})
}()
c.JSON(http.StatusOK, apiKey)
}
func (api *API) getAPIKeys(c *gin.Context) {
apiKeys, err := api.KeyService.GetAllKeys()
if err != nil {
c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, apiKeys)
}
func (api *API) deleteAPIKey(c *gin.Context) {
keyName := c.Query("name")
err := api.KeyService.DeleteKey(keyName)
if err != nil {
c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Deleted api key!"})
}