Files
gpt-load/internal/control/auth.go
T

97 lines
2.2 KiB
Go

package control
import (
"crypto/sha256"
"fmt"
"net/netip"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
app_errors "gpt-load/internal/platform/errors"
"gpt-load/internal/platform/response"
)
type authLockedData struct {
RetryAfterSeconds int64 `json:"retry_after_seconds"`
}
type authSessionResponse struct {
Authenticated bool `json:"authenticated"`
}
func normalizePeerIP(remoteAddr string) (string, error) {
endpoint, err := netip.ParseAddrPort(strings.TrimSpace(remoteAddr))
if err != nil {
return "", fmt.Errorf("parse remote peer endpoint: %w", err)
}
return endpoint.Addr().Unmap().WithZone("").String(), nil
}
func retryAfterSeconds(remaining time.Duration) int64 {
seconds := int64((remaining + time.Second - 1) / time.Second)
if seconds < 1 {
return 1
}
return seconds
}
func (s *Server) authenticate() gin.HandlerFunc {
return func(c *gin.Context) {
peer, err := normalizePeerIP(c.Request.RemoteAddr)
if err != nil {
logServiceError("authenticate_peer", err, app_errors.ErrInternalServer.Code)
response.ErrorI18nFromAPIError(
c,
app_errors.ErrInternalServer,
"internal_error",
)
c.Abort()
return
}
fields := strings.Fields(c.GetHeader("Authorization"))
formatValid := len(fields) == 2 &&
strings.EqualFold(fields[0], "Bearer")
token := ""
if formatValid {
token = fields[1]
}
requestDigest := sha256.Sum256([]byte(token))
matches := s.compareDigest(requestDigest[:], s.authDigest[:]) == 1
decision := s.authFailures.evaluate(peer, formatValid && matches)
if decision.retryAfter > 0 {
seconds := retryAfterSeconds(decision.retryAfter)
c.Header("Retry-After", strconv.FormatInt(seconds, 10))
apiErr := app_errors.NewAPIErrorWithData(
app_errors.ErrAuthLocked,
authLockedData{RetryAfterSeconds: seconds},
)
response.ErrorI18nFromAPIError(c, apiErr, "auth.locked")
c.Abort()
return
}
if !decision.authorized {
response.ErrorI18nFromAPIError(
c,
app_errors.ErrUnauthorized,
"auth.invalid_key",
)
c.Abort()
return
}
c.Next()
}
}
func (s *Server) handleAuthSession(c *gin.Context) {
response.SuccessI18n(
c,
"common.success",
authSessionResponse{Authenticated: true},
)
}