Add comprehensive human-readable comments to all Go files
- Comments explain what each function does and why - Fixed unused strconv import in queue.go - Better documentation for SMTP email service - Explained journalctl log parsing in logs.go - Clarified queue operations in queue.go
This commit is contained in:
parent
3e3620a24f
commit
68285d861a
9 changed files with 728 additions and 203 deletions
|
|
@ -1,85 +1,127 @@
|
|||
// Package auth handles authentication and authorization for the application.
|
||||
// It provides JWT token management and password hashing.
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
"errors" // standard errors package
|
||||
"time" // time handling
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"github.com/golang-jwt/jwt/v5" // JWT library for token handling
|
||||
"golang.org/x/crypto/bcrypt" // bcrypt for secure password hashing
|
||||
)
|
||||
|
||||
// Custom error types for JWT operations.
|
||||
var (
|
||||
ErrInvalidToken = errors.New("invalid token")
|
||||
ErrExpiredToken = errors.New("token has expired")
|
||||
ErrInvalidToken = errors.New("invalid token") // Token is malformed or has invalid signature
|
||||
ErrExpiredToken = errors.New("token has expired") // Token has expired
|
||||
)
|
||||
|
||||
// Claims represents the data stored in a JWT token.
|
||||
// JWT tokens are signed payloads that can be verified but not tampered with.
|
||||
type Claims struct {
|
||||
UserID uint `json:"userId"`
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
jwt.RegisteredClaims
|
||||
UserID uint `json:"userId"` // ID of the logged-in user
|
||||
Username string `json:"username"` // Username for display
|
||||
Role string `json:"role"` // User's role ("admin" or "user")
|
||||
jwt.RegisteredClaims // Standard JWT claims (expiration, issuer, etc.)
|
||||
}
|
||||
|
||||
// JWTManager handles creating and validating JWT tokens.
|
||||
type JWTManager struct {
|
||||
secretKey []byte
|
||||
issuer string
|
||||
secretKey []byte // Secret key for signing/verifying tokens (keep this secret!)
|
||||
issuer string // Token issuer (used to verify token source)
|
||||
}
|
||||
|
||||
// NewJWTManager creates a new JWT manager.
|
||||
// secretKey: the secret key for signing tokens. Should be long and random.
|
||||
// issuer: the token issuer string (usually the app name).
|
||||
func NewJWTManager(secretKey, issuer string) *JWTManager {
|
||||
return &JWTManager{
|
||||
secretKey: []byte(secretKey),
|
||||
secretKey: []byte(secretKey), // Convert string to bytes for signing
|
||||
issuer: issuer,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateToken creates a new JWT token for a user.
|
||||
// userID: the user's ID to include in the token.
|
||||
// username: the username to include for display.
|
||||
// role: the user's role ("admin" or "user").
|
||||
// duration: how long the token should be valid.
|
||||
// Returns: the signed token string and any error.
|
||||
func (j *JWTManager) GenerateToken(userID uint, username, role string, duration time.Duration) (string, error) {
|
||||
// Create the claims (payload) for the token.
|
||||
claims := &Claims{
|
||||
UserID: userID,
|
||||
Username: username,
|
||||
Role: role,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(duration)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: j.issuer,
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(duration)), // Token expires in "duration"
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()), // When the token was created
|
||||
Issuer: j.issuer, // Who issued the token
|
||||
},
|
||||
}
|
||||
|
||||
// Create a new token with HMAC-SHA256 signing.
|
||||
// HMAC is a symmetric algorithm - same key for signing and verifying.
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
// Sign the token with our secret key and return the string representation.
|
||||
return token.SignedString(j.secretKey)
|
||||
}
|
||||
|
||||
// ValidateToken checks if a token is valid and returns its claims.
|
||||
// tokenString: the JWT token string to validate.
|
||||
// Returns: the claims if valid, or an error if invalid.
|
||||
func (j *JWTManager) ValidateToken(tokenString string) (*Claims, error) {
|
||||
// Parse and validate the token.
|
||||
// The callback function is called to get the signing key.
|
||||
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
// Verify the signing method is HMAC (not RSA, ECDSA, etc.).
|
||||
// This prevents algorithm confusion attacks.
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
return j.secretKey, nil
|
||||
return j.secretKey, nil // Return our secret key for verification
|
||||
})
|
||||
|
||||
// Handle parsing errors.
|
||||
if err != nil {
|
||||
// Check specifically for expiration.
|
||||
if errors.Is(err, jwt.ErrTokenExpired) {
|
||||
return nil, ErrExpiredToken
|
||||
}
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
// Extract and validate claims.
|
||||
claims, ok := token.Claims.(*Claims)
|
||||
if !ok || !token.Valid {
|
||||
if !ok || !token.Valid { // token.Valid is false if signature verification failed
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
// HashPassword creates a bcrypt hash of a password.
|
||||
// password: the plain-text password to hash.
|
||||
// Returns: the bcrypt hash and any error.
|
||||
// Uses bcrypt's default cost factor (currently 10).
|
||||
func HashPassword(password string) (string, error) {
|
||||
// GenerateFromPassword creates a bcrypt hash.
|
||||
// bcrypt is designed to be slow to resist brute-force attacks.
|
||||
// DefaultCost is a good balance between security and performance.
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(hash), nil
|
||||
return string(hash), nil // Convert bytes to string
|
||||
}
|
||||
|
||||
// CheckPassword verifies a password against a bcrypt hash.
|
||||
// password: the plain-text password to check.
|
||||
// hash: the bcrypt hash to verify against.
|
||||
// Returns: true if the password matches, false otherwise.
|
||||
func CheckPassword(password, hash string) bool {
|
||||
// CompareHashAndPassword returns nil if they match, error otherwise.
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
package auth
|
||||
|
||||
// Role represents a user's role in the system.
|
||||
type Role string
|
||||
|
||||
// Predefined roles.
|
||||
const (
|
||||
RoleAdmin Role = "admin"
|
||||
RoleUser Role = "user"
|
||||
RoleAdmin Role = "admin" // Full access to all features
|
||||
RoleUser Role = "user" // Limited access (not currently used)
|
||||
)
|
||||
|
||||
// Context holds the authenticated user's information.
|
||||
// This is stored in the request context during authentication.
|
||||
type Context struct {
|
||||
UserID uint
|
||||
Username string
|
||||
Role Role
|
||||
UserID uint // The user's database ID
|
||||
Username string // The user's username for display
|
||||
Role Role // The user's role (admin or user)
|
||||
}
|
||||
|
||||
// IsAdmin checks if the user has admin privileges.
|
||||
func (c *Context) IsAdmin() bool {
|
||||
return c.Role == RoleAdmin
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue