Compare commits
7 Commits
v0.10.5
...
2a863c51e9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a863c51e9 | ||
|
|
a70d923a6a | ||
|
|
f660511b63 | ||
|
|
044af60699 | ||
|
|
384036bcdd | ||
|
|
ef300d233b | ||
|
|
edad65d6a9 |
93
cmd/steam-crypt/main.go
Normal file
93
cmd/steam-crypt/main.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"acc-server-manager/local/model"
|
||||||
|
"acc-server-manager/local/utl/configs"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
encrypt = flag.Bool("encrypt", false, "Encrypt a password")
|
||||||
|
decrypt = flag.Bool("decrypt", false, "Decrypt a password")
|
||||||
|
password = flag.String("password", "", "Password to encrypt/decrypt")
|
||||||
|
help = flag.Bool("help", false, "Show help")
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *help || (!*encrypt && !*decrypt) {
|
||||||
|
showHelp()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *encrypt && *decrypt {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: Cannot specify both -encrypt and -decrypt\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *password == "" {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: Password is required\n")
|
||||||
|
showHelp()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize configs to load encryption key
|
||||||
|
configs.Init()
|
||||||
|
|
||||||
|
if *encrypt {
|
||||||
|
encrypted, err := model.EncryptPassword(*password)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error encrypting password: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println(encrypted)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *decrypt {
|
||||||
|
decrypted, err := model.DecryptPassword(*password)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error decrypting password: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println(decrypted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func showHelp() {
|
||||||
|
fmt.Println("Steam Credentials Encryption/Decryption Utility")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("This utility encrypts and decrypts Steam credentials using the same")
|
||||||
|
fmt.Println("AES-256-GCM encryption used by the ACC Server Manager application.")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Usage:")
|
||||||
|
fmt.Println(" steam-crypt -encrypt -password \"your_password\"")
|
||||||
|
fmt.Println(" steam-crypt -decrypt -password \"encrypted_string\"")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Options:")
|
||||||
|
fmt.Println(" -encrypt Encrypt the provided password")
|
||||||
|
fmt.Println(" -decrypt Decrypt the provided encrypted string")
|
||||||
|
fmt.Println(" -password The password to encrypt or encrypted string to decrypt")
|
||||||
|
fmt.Println(" -help Show this help message")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Environment Variables Required:")
|
||||||
|
fmt.Println(" ENCRYPTION_KEY - 32-byte encryption key (same as main application)")
|
||||||
|
fmt.Println(" APP_SECRET - Application secret (required by configs)")
|
||||||
|
fmt.Println(" APP_SECRET_CODE - Application secret code (required by configs)")
|
||||||
|
fmt.Println(" ACCESS_KEY - Access key (required by configs)")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Examples:")
|
||||||
|
fmt.Println(" # Encrypt a password")
|
||||||
|
fmt.Println(" steam-crypt -encrypt -password \"mysteampassword\"")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println(" # Decrypt an encrypted password")
|
||||||
|
fmt.Println(" steam-crypt -decrypt -password \"base64encryptedstring\"")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Security Notes:")
|
||||||
|
fmt.Println(" - The encryption key must be exactly 32 bytes for AES-256")
|
||||||
|
fmt.Println(" - Uses AES-256-GCM for authenticated encryption")
|
||||||
|
fmt.Println(" - Each encryption includes a unique nonce for security")
|
||||||
|
fmt.Println(" - Passwords are validated for length and basic security")
|
||||||
|
}
|
||||||
@@ -95,7 +95,7 @@ func (c *MembershipController) Login(ctx *fiber.Ctx) error {
|
|||||||
// @Failure 500 {object} error_handler.ErrorResponse "Internal server error"
|
// @Failure 500 {object} error_handler.ErrorResponse "Internal server error"
|
||||||
// @Router /auth/open-token [post]
|
// @Router /auth/open-token [post]
|
||||||
func (c *MembershipController) GenerateOpenToken(ctx *fiber.Ctx) error {
|
func (c *MembershipController) GenerateOpenToken(ctx *fiber.Ctx) error {
|
||||||
token, err := c.service.GenerateOpenToken(ctx.UserContext(), ctx.Locals("userId").(string))
|
token, err := c.service.GenerateOpenToken(ctx.UserContext(), ctx.Locals("userID").(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.errorHandler.HandleAuthError(ctx, err)
|
return c.errorHandler.HandleAuthError(ctx, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,13 @@ func (m *AuthMiddleware) AuthenticateWithHandler(jwtHandler *jwt.JWTHandler, isO
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !jwtHandler.IsOpenToken && claims.IsOpenToken {
|
||||||
|
logging.Error("Authentication failed: attempting to authenticate with open token")
|
||||||
|
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||||
|
"error": "Wrong token type used",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Additional security: validate user ID format
|
// Additional security: validate user ID format
|
||||||
if claims.UserID == "" || len(claims.UserID) < 10 {
|
if claims.UserID == "" || len(claims.UserID) < 10 {
|
||||||
logging.Error("Authentication failed: invalid user ID in token from IP %s", ip)
|
logging.Error("Authentication failed: invalid user ID in token from IP %s", ip)
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ func (r *StateHistoryRepository) GetRecentSessions(ctx context.Context, filter *
|
|||||||
FROM state_histories
|
FROM state_histories
|
||||||
WHERE server_id = ? AND date_created BETWEEN ? AND ?
|
WHERE server_id = ? AND date_created BETWEEN ? AND ?
|
||||||
GROUP BY session_id
|
GROUP BY session_id
|
||||||
HAVING COUNT(*) > 1 AND MAX(player_count) > 0
|
HAVING MAX(player_count) > 0
|
||||||
ORDER BY date DESC
|
ORDER BY date DESC
|
||||||
LIMIT 10
|
LIMIT 10
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -63,16 +63,11 @@ func (s *MembershipService) Login(ctx context.Context, username, password string
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.jwtHandler.GenerateToken(user)
|
return s.jwtHandler.GenerateToken(user.ID.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MembershipService) GenerateOpenToken(ctx context.Context, userId string) (string, error) {
|
func (s *MembershipService) GenerateOpenToken(ctx context.Context, userId string) (string, error) {
|
||||||
user, err := s.repo.GetByID(ctx, userId)
|
return s.openJwtHandler.GenerateToken(userId)
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.openJwtHandler.GenerateToken(user)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser creates a new user.
|
// CreateUser creates a new user.
|
||||||
|
|||||||
@@ -124,33 +124,44 @@ func (s *SteamService) InstallServer(ctx context.Context, installPath string, se
|
|||||||
// Get SteamCMD path from environment variable
|
// Get SteamCMD path from environment variable
|
||||||
steamCMDPath := env.GetSteamCMDPath()
|
steamCMDPath := env.GetSteamCMDPath()
|
||||||
|
|
||||||
// Build SteamCMD command
|
// Build SteamCMD command arguments
|
||||||
args := []string{
|
steamCMDArgs := []string{
|
||||||
"-nologo",
|
|
||||||
"-noprofile",
|
|
||||||
steamCMDPath,
|
|
||||||
"+force_install_dir", absPath,
|
"+force_install_dir", absPath,
|
||||||
"+login",
|
"+login",
|
||||||
}
|
}
|
||||||
|
|
||||||
if creds != nil && creds.Username != "" {
|
if creds != nil && creds.Username != "" {
|
||||||
args = append(args, creds.Username)
|
steamCMDArgs = append(steamCMDArgs, creds.Username)
|
||||||
if creds.Password != "" {
|
if creds.Password != "" {
|
||||||
args = append(args, creds.Password)
|
steamCMDArgs = append(steamCMDArgs, creds.Password)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
args = append(args, "anonymous")
|
steamCMDArgs = append(steamCMDArgs, "anonymous")
|
||||||
}
|
}
|
||||||
|
|
||||||
args = append(args,
|
steamCMDArgs = append(steamCMDArgs,
|
||||||
"+app_update", ACCServerAppID,
|
"+app_update", ACCServerAppID,
|
||||||
"validate",
|
"validate",
|
||||||
"+quit",
|
"+quit",
|
||||||
)
|
)
|
||||||
|
|
||||||
// Use interactive executor to handle potential 2FA prompts
|
// Build PowerShell arguments to execute SteamCMD directly
|
||||||
|
// This matches the format: powershell -nologo -noprofile c:\steamcmd\steamcmd.exe +args...
|
||||||
|
args := []string{"-nologo", "-noprofile"}
|
||||||
|
args = append(args, steamCMDPath)
|
||||||
|
args = append(args, steamCMDArgs...)
|
||||||
|
|
||||||
|
// Use interactive executor to handle potential 2FA prompts with timeout
|
||||||
logging.Info("Installing ACC server to %s...", absPath)
|
logging.Info("Installing ACC server to %s...", absPath)
|
||||||
if err := s.interactiveExecutor.ExecuteInteractive(ctx, serverID, args...); err != nil {
|
|
||||||
|
// Create a context with timeout to prevent hanging indefinitely
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := s.interactiveExecutor.ExecuteInteractive(timeoutCtx, serverID, args...); err != nil {
|
||||||
|
if timeoutCtx.Err() == context.DeadlineExceeded {
|
||||||
|
return fmt.Errorf("SteamCMD operation timed out after 10 minutes")
|
||||||
|
}
|
||||||
return fmt.Errorf("failed to run SteamCMD: %v", err)
|
return fmt.Errorf("failed to run SteamCMD: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,14 +61,33 @@ func (e *InteractiveCommandExecutor) ExecuteInteractive(ctx context.Context, ser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create channels for output monitoring
|
// Create channels for output monitoring
|
||||||
outputDone := make(chan error)
|
outputDone := make(chan error, 1)
|
||||||
|
cmdDone := make(chan error, 1)
|
||||||
|
|
||||||
// Monitor stdout and stderr for 2FA prompts
|
// Monitor stdout and stderr for 2FA prompts
|
||||||
go e.monitorOutput(ctx, stdout, stderr, serverID, outputDone)
|
go e.monitorOutput(ctx, stdout, stderr, serverID, outputDone)
|
||||||
|
|
||||||
// Wait for either the command to finish or output monitoring to complete
|
// Wait for the command to finish in a separate goroutine
|
||||||
cmdErr := cmd.Wait()
|
go func() {
|
||||||
outputErr := <-outputDone
|
cmdDone <- cmd.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for both command and output monitoring to complete
|
||||||
|
var cmdErr, outputErr error
|
||||||
|
completedCount := 0
|
||||||
|
|
||||||
|
for completedCount < 2 {
|
||||||
|
select {
|
||||||
|
case cmdErr = <-cmdDone:
|
||||||
|
completedCount++
|
||||||
|
logging.Info("Command execution completed")
|
||||||
|
case outputErr = <-outputDone:
|
||||||
|
completedCount++
|
||||||
|
logging.Info("Output monitoring completed")
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if outputErr != nil {
|
if outputErr != nil {
|
||||||
logging.Warn("Output monitoring error: %v", outputErr)
|
logging.Warn("Output monitoring error: %v", outputErr)
|
||||||
@@ -78,45 +97,85 @@ func (e *InteractiveCommandExecutor) ExecuteInteractive(ctx context.Context, ser
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *InteractiveCommandExecutor) monitorOutput(ctx context.Context, stdout, stderr io.Reader, serverID *uuid.UUID, done chan error) {
|
func (e *InteractiveCommandExecutor) monitorOutput(ctx context.Context, stdout, stderr io.Reader, serverID *uuid.UUID, done chan error) {
|
||||||
defer close(done)
|
defer func() {
|
||||||
|
select {
|
||||||
|
case done <- nil:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Create scanners for both outputs
|
// Create scanners for both outputs
|
||||||
stdoutScanner := bufio.NewScanner(stdout)
|
stdoutScanner := bufio.NewScanner(stdout)
|
||||||
stderrScanner := bufio.NewScanner(stderr)
|
stderrScanner := bufio.NewScanner(stderr)
|
||||||
|
|
||||||
outputChan := make(chan string)
|
outputChan := make(chan string, 100) // Buffered channel to prevent blocking
|
||||||
|
readersDone := make(chan struct{}, 2)
|
||||||
|
|
||||||
// Read from stdout
|
// Read from stdout
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() { readersDone <- struct{}{} }()
|
||||||
for stdoutScanner.Scan() {
|
for stdoutScanner.Scan() {
|
||||||
line := stdoutScanner.Text()
|
line := stdoutScanner.Text()
|
||||||
if e.LogOutput {
|
if e.LogOutput {
|
||||||
logging.Info("STDOUT: %s", line)
|
logging.Info("STDOUT: %s", line)
|
||||||
}
|
}
|
||||||
outputChan <- line
|
select {
|
||||||
|
case outputChan <- line:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := stdoutScanner.Err(); err != nil {
|
||||||
|
logging.Warn("Stdout scanner error: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Read from stderr
|
// Read from stderr
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() { readersDone <- struct{}{} }()
|
||||||
for stderrScanner.Scan() {
|
for stderrScanner.Scan() {
|
||||||
line := stderrScanner.Text()
|
line := stderrScanner.Text()
|
||||||
if e.LogOutput {
|
if e.LogOutput {
|
||||||
logging.Info("STDERR: %s", line)
|
logging.Info("STDERR: %s", line)
|
||||||
}
|
}
|
||||||
outputChan <- line
|
select {
|
||||||
|
case outputChan <- line:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := stderrScanner.Err(); err != nil {
|
||||||
|
logging.Warn("Stderr scanner error: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Monitor for 2FA prompts
|
// Monitor for completion and 2FA prompts
|
||||||
|
readersFinished := 0
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
done <- ctx.Err()
|
done <- ctx.Err()
|
||||||
return
|
return
|
||||||
|
case <-readersDone:
|
||||||
|
readersFinished++
|
||||||
|
if readersFinished == 2 {
|
||||||
|
// Both readers are done, close output channel and finish monitoring
|
||||||
|
close(outputChan)
|
||||||
|
// Drain any remaining output
|
||||||
|
for line := range outputChan {
|
||||||
|
if e.is2FAPrompt(line) {
|
||||||
|
if err := e.handle2FAPrompt(ctx, line, serverID); err != nil {
|
||||||
|
logging.Error("Failed to handle 2FA prompt: %v", err)
|
||||||
|
done <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
case line, ok := <-outputChan:
|
case line, ok := <-outputChan:
|
||||||
if !ok {
|
if !ok {
|
||||||
done <- nil
|
// Channel closed, we're done
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Version = "0.10.5"
|
Version = "0.10.7"
|
||||||
Prefix = "v1"
|
Prefix = "v1"
|
||||||
Secret string
|
Secret string
|
||||||
SecretCode string
|
SecretCode string
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import (
|
|||||||
|
|
||||||
// Claims represents the JWT claims.
|
// Claims represents the JWT claims.
|
||||||
type Claims struct {
|
type Claims struct {
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
|
IsOpenToken bool `json:"is_open_token"`
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,13 +71,14 @@ func (jh *JWTHandler) GenerateSecretKey() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GenerateToken generates a new JWT for a given user.
|
// GenerateToken generates a new JWT for a given user.
|
||||||
func (jh *JWTHandler) GenerateToken(user *model.User) (string, error) {
|
func (jh *JWTHandler) GenerateToken(userId string) (string, error) {
|
||||||
expirationTime := time.Now().Add(24 * time.Hour)
|
expirationTime := time.Now().Add(24 * time.Hour)
|
||||||
claims := &Claims{
|
claims := &Claims{
|
||||||
UserID: user.ID.String(),
|
UserID: userId,
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
||||||
},
|
},
|
||||||
|
IsOpenToken: jh.IsOpenToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
@@ -90,6 +92,7 @@ func (jh *JWTHandler) GenerateTokenWithExpiry(user *model.User, expiry time.Time
|
|||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
||||||
},
|
},
|
||||||
|
IsOpenToken: jh.IsOpenToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ type PathValidator struct {
|
|||||||
func NewPathValidator() *PathValidator {
|
func NewPathValidator() *PathValidator {
|
||||||
blockedPatterns := []*regexp.Regexp{
|
blockedPatterns := []*regexp.Regexp{
|
||||||
regexp.MustCompile(`\.\.`),
|
regexp.MustCompile(`\.\.`),
|
||||||
regexp.MustCompile(`[<>:"|?*]`),
|
|
||||||
regexp.MustCompile(`^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$`),
|
regexp.MustCompile(`^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$`),
|
||||||
regexp.MustCompile(`\x00`),
|
regexp.MustCompile(`\x00`),
|
||||||
regexp.MustCompile(`^\\\\`),
|
regexp.MustCompile(`^\\\\`),
|
||||||
@@ -92,4 +91,4 @@ func (pv *PathValidator) AddAllowedBasePath(path string) error {
|
|||||||
|
|
||||||
func (pv *PathValidator) GetAllowedBasePaths() []string {
|
func (pv *PathValidator) GetAllowedBasePaths() []string {
|
||||||
return append([]string(nil), pv.allowedBasePaths...)
|
return append([]string(nil), pv.allowedBasePaths...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func GenerateTestToken() (string, error) {
|
|||||||
jwtHandler := jwt.NewJWTHandler(testSecret)
|
jwtHandler := jwt.NewJWTHandler(testSecret)
|
||||||
|
|
||||||
// Generate JWT token
|
// Generate JWT token
|
||||||
token, err := jwtHandler.GenerateToken(user)
|
token, err := jwtHandler.GenerateToken(user.ID.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to generate test token: %w", err)
|
return "", fmt.Errorf("failed to generate test token: %w", err)
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ func GenerateTestTokenWithExpiry(expiryTime time.Time) (string, error) {
|
|||||||
testSecret = "test-secret-that-is-at-least-32-bytes-long-for-security"
|
testSecret = "test-secret-that-is-at-least-32-bytes-long-for-security"
|
||||||
}
|
}
|
||||||
jwtHandler := jwt.NewJWTHandler(testSecret)
|
jwtHandler := jwt.NewJWTHandler(testSecret)
|
||||||
|
|
||||||
// Create test user
|
// Create test user
|
||||||
user := &model.User{
|
user := &model.User{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func TestJWT_GenerateAndValidateToken(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test JWT generation
|
// Test JWT generation
|
||||||
token, err := jwtHandler.GenerateToken(user)
|
token, err := jwtHandler.GenerateToken(user.ID.String())
|
||||||
tests.AssertNoError(t, err)
|
tests.AssertNoError(t, err)
|
||||||
tests.AssertNotNil(t, token)
|
tests.AssertNotNil(t, token)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user