mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-03 18:22:57 +00:00
upload
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
// JWTClaims represents the JWT claims structure
|
||||
type JWTClaims struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Email string `json:"email"`
|
||||
Role string `json:"role"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// GenerateJWT generates a new JWT token for the given user
|
||||
func GenerateJWT(userID uint, email, role string) (string, error) {
|
||||
expirationTime := time.Now().Add(24 * time.Hour)
|
||||
|
||||
claims := &JWTClaims{
|
||||
UserID: userID,
|
||||
Email: email,
|
||||
Role: role,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: "fotbal-club",
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString([]byte(GetJWTSecret()))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
// ParseJWT parses and validates a JWT token
|
||||
func ParseJWT(tokenString string) (*JWTClaims, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(GetJWTSecret()), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
|
||||
// GetJWTSecret returns the JWT secret from environment variable or a default value
|
||||
func GetJWTSecret() string {
|
||||
secret := os.Getenv("JWT_SECRET")
|
||||
if secret == "" {
|
||||
return "default-secret-key-change-in-production"
|
||||
}
|
||||
return secret
|
||||
}
|
||||
|
||||
// GetUserFromContext gets the authenticated user from Gin context
|
||||
func GetUserFromContext(c *gin.Context) (*JWTClaims, error) {
|
||||
// Preferred: claims set by auth middleware
|
||||
if v, ok := c.Get("claims"); ok {
|
||||
if cl, ok2 := v.(*JWTClaims); ok2 {
|
||||
return cl, nil
|
||||
}
|
||||
}
|
||||
// Fallback: parse Authorization header (Bearer <token>)
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
return nil, errors.New("authorization header missing")
|
||||
}
|
||||
parts := strings.Split(authHeader, " ")
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return nil, errors.New("invalid authorization header format")
|
||||
}
|
||||
token := parts[1]
|
||||
cl, err := ParseJWT(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cl, nil
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package utils
|
||||
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
// HashPassword hashes a password using bcrypt
|
||||
func HashPassword(password string) (string, error) {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(hashedPassword), nil
|
||||
}
|
||||
|
||||
// CheckPassword verifies a password against a hash
|
||||
func CheckPassword(password, hashedPassword string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SanitizeHTML removes potentially dangerous HTML tags and attributes
|
||||
// This is a basic implementation. For production, consider using bluemonday library
|
||||
func SanitizeHTML(html string) string {
|
||||
// Remove script tags and content
|
||||
reScript := regexp.MustCompile(`(?i)<script[^>]*>.*?</script>`)
|
||||
html = reScript.ReplaceAllString(html, "")
|
||||
|
||||
// Remove inline event handlers (onclick, onerror, etc.)
|
||||
reEvents := regexp.MustCompile(`(?i)\s*on\w+\s*=\s*["'][^"']*["']`)
|
||||
html = reEvents.ReplaceAllString(html, "")
|
||||
|
||||
// Remove javascript: URLs
|
||||
reJSURL := regexp.MustCompile(`(?i)javascript:`)
|
||||
html = reJSURL.ReplaceAllString(html, "")
|
||||
|
||||
// Remove iframe tags (can be optionally allowed if needed)
|
||||
reIframe := regexp.MustCompile(`(?i)<iframe[^>]*>.*?</iframe>`)
|
||||
html = reIframe.ReplaceAllString(html, "")
|
||||
|
||||
// Remove object/embed tags
|
||||
reObject := regexp.MustCompile(`(?i)<(object|embed)[^>]*>.*?</\1>`)
|
||||
html = reObject.ReplaceAllString(html, "")
|
||||
|
||||
// Remove style tags (if CSS injection is a concern)
|
||||
// Uncomment if you want to remove inline styles
|
||||
// reStyle := regexp.MustCompile(`(?i)<style[^>]*>.*?</style>`)
|
||||
// html = reStyle.ReplaceAllString(html, "")
|
||||
|
||||
return strings.TrimSpace(html)
|
||||
}
|
||||
|
||||
// SanitizeString removes HTML tags entirely and returns plain text
|
||||
func SanitizeString(input string) string {
|
||||
// Remove all HTML tags
|
||||
reHTML := regexp.MustCompile(`<[^>]*>`)
|
||||
text := reHTML.ReplaceAllString(input, " ")
|
||||
|
||||
// Normalize whitespace
|
||||
text = strings.Join(strings.Fields(text), " ")
|
||||
|
||||
return strings.TrimSpace(text)
|
||||
}
|
||||
|
||||
// ValidateURL checks if a URL is safe (http/https only)
|
||||
func ValidateURL(url string) bool {
|
||||
if url == "" {
|
||||
return true
|
||||
}
|
||||
lower := strings.ToLower(strings.TrimSpace(url))
|
||||
return strings.HasPrefix(lower, "http://") || strings.HasPrefix(lower, "https://") || strings.HasPrefix(lower, "/")
|
||||
}
|
||||
|
||||
// SanitizeFilename removes dangerous characters from filenames
|
||||
func SanitizeFilename(filename string) string {
|
||||
// Remove path traversal attempts
|
||||
filename = strings.ReplaceAll(filename, "..", "")
|
||||
filename = strings.ReplaceAll(filename, "/", "")
|
||||
filename = strings.ReplaceAll(filename, "\\", "")
|
||||
|
||||
// Allow only safe characters
|
||||
re := regexp.MustCompile(`[^a-zA-Z0-9._-]`)
|
||||
filename = re.ReplaceAllString(filename, "_")
|
||||
|
||||
// Limit length
|
||||
if len(filename) > 200 {
|
||||
filename = filename[:200]
|
||||
}
|
||||
|
||||
return filename
|
||||
}
|
||||
|
||||
// RemoveNullBytes removes null bytes that can cause issues
|
||||
func RemoveNullBytes(s string) string {
|
||||
return strings.ReplaceAll(s, "\x00", "")
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
type SubscriberTokenClaims struct {
|
||||
Email string `json:"email"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// GenerateSubscriberToken creates a signed token for a subscriber email, expires in `ttl` minutes
|
||||
func GenerateSubscriberToken(email string, ttlMinutes int) (string, error) {
|
||||
if ttlMinutes <= 0 {
|
||||
ttlMinutes = 60
|
||||
}
|
||||
now := time.Now()
|
||||
claims := &SubscriberTokenClaims{
|
||||
Email: email,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
IssuedAt: jwt.NewNumericDate(now),
|
||||
ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(ttlMinutes) * time.Minute)),
|
||||
Issuer: "fotbal-club",
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString([]byte(getJWTSecret()))
|
||||
}
|
||||
|
||||
// ParseSubscriberToken validates and returns the email embedded in the token
|
||||
func ParseSubscriberToken(tokenString string) (string, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &SubscriberTokenClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(getJWTSecret()), nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if claims, ok := token.Claims.(*SubscriberTokenClaims); ok && token.Valid {
|
||||
return claims.Email, nil
|
||||
}
|
||||
return "", jwt.ErrTokenMalformed
|
||||
}
|
||||
|
||||
func getJWTSecret() string {
|
||||
s := os.Getenv("JWT_SECRET")
|
||||
if s == "" {
|
||||
return "default-secret-key-change-in-production"
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user