* Replaces old Deno server with a Go implementation * Restructured the code base accordingly
260 lines
6.9 KiB
Go
260 lines
6.9 KiB
Go
package server
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestCreateAuthenticator_NewFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
authFile := filepath.Join(tmpDir, "auth.json")
|
|
|
|
authOptions := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "testpass",
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
auth, err := CreateAuthenticator(authFile, authOptions)
|
|
assert.NoError(t, err, "Should create new authenticator without error")
|
|
assert.NotNil(t, auth, "Authenticator should not be nil")
|
|
assert.NotEmpty(t, auth.SecretKey, "Secret key should be generated")
|
|
assert.NotEmpty(t, auth.AuthHash, "Auth hash should be generated")
|
|
|
|
// Verify file was created
|
|
_, err = os.Stat(authFile)
|
|
assert.False(t, os.IsNotExist(err), "Auth file should be created")
|
|
}
|
|
|
|
func TestCreateAuthenticator_ExistingFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
authFile := filepath.Join(tmpDir, "auth.json")
|
|
|
|
// Create initial authenticator
|
|
authOptions := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "testpass",
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
auth1, err := CreateAuthenticator(authFile, authOptions)
|
|
require.NoError(t, err)
|
|
originalKey := auth1.SecretKey
|
|
|
|
// Load from existing file with same options
|
|
auth2, err := CreateAuthenticator(authFile, authOptions)
|
|
assert.NoError(t, err, "Should load existing authenticator without error")
|
|
assert.Equal(t, originalKey, auth2.SecretKey, "Secret key should remain the same")
|
|
}
|
|
|
|
func TestCreateAuthenticator_AuthOptionsChanged(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
authFile := filepath.Join(tmpDir, "auth.json")
|
|
|
|
// Create initial authenticator
|
|
authOptions1 := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "testpass",
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
auth1, err := CreateAuthenticator(authFile, authOptions1)
|
|
require.NoError(t, err)
|
|
originalKey := auth1.SecretKey
|
|
|
|
// Load with different auth options
|
|
authOptions2 := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "newpass", // Changed password
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
auth2, err := CreateAuthenticator(authFile, authOptions2)
|
|
assert.NoError(t, err, "Should load authenticator with changed options")
|
|
assert.NotEqual(t, originalKey, auth2.SecretKey, "Secret key should change when auth options change")
|
|
}
|
|
|
|
func TestAuthenticator_CreateJWT(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
authFile := filepath.Join(tmpDir, "auth.json")
|
|
|
|
authOptions := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "testpass",
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
auth, err := CreateAuthenticator(authFile, authOptions)
|
|
require.NoError(t, err)
|
|
|
|
payload := map[string]any{
|
|
"user": "testuser",
|
|
"role": "admin",
|
|
}
|
|
|
|
token, err := auth.CreateJWT(payload)
|
|
assert.NoError(t, err, "Should create JWT without error")
|
|
assert.NotEmpty(t, token, "Token should not be empty")
|
|
|
|
// Verify token structure (should have 3 parts separated by dots)
|
|
parts := len([]rune(token))
|
|
assert.Greater(t, parts, 0, "Token should not be empty")
|
|
}
|
|
|
|
func TestAuthenticator_CreateJWT_WithExpiry(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
authFile := filepath.Join(tmpDir, "auth.json")
|
|
|
|
authOptions := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "testpass",
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
auth, err := CreateAuthenticator(authFile, authOptions)
|
|
require.NoError(t, err)
|
|
|
|
payload := map[string]any{
|
|
"user": "testuser",
|
|
}
|
|
|
|
token, err := auth.CreateJWT(payload, 3600) // 1 hour expiry
|
|
assert.NoError(t, err, "Should create JWT with expiry without error")
|
|
assert.NotEmpty(t, token, "Token should not be empty")
|
|
|
|
// Verify the token and check expiry
|
|
claims, err := auth.VerifyAndDecodeJWT(token)
|
|
assert.NoError(t, err, "Should verify token")
|
|
|
|
exp, ok := claims["exp"]
|
|
assert.True(t, ok, "Token should have expiry claim")
|
|
assert.NotNil(t, exp, "Expiry should not be nil")
|
|
}
|
|
|
|
func TestAuthenticator_VerifyAndDecodeJWT(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
authFile := filepath.Join(tmpDir, "auth.json")
|
|
|
|
authOptions := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "testpass",
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
auth, err := CreateAuthenticator(authFile, authOptions)
|
|
require.NoError(t, err)
|
|
|
|
payload := map[string]any{
|
|
"user": "testuser",
|
|
"role": "admin",
|
|
"id": 123,
|
|
}
|
|
|
|
token, err := auth.CreateJWT(payload)
|
|
require.NoError(t, err)
|
|
|
|
// Verify and decode the token
|
|
claims, err := auth.VerifyAndDecodeJWT(token)
|
|
assert.NoError(t, err, "Should verify and decode JWT without error")
|
|
assert.Equal(t, "testuser", claims["user"], "Should preserve user claim")
|
|
assert.Equal(t, "admin", claims["role"], "Should preserve role claim")
|
|
assert.Equal(t, float64(123), claims["id"], "Should preserve id claim")
|
|
}
|
|
|
|
func TestAuthenticator_VerifyAndDecodeJWT_InvalidToken(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
authFile := filepath.Join(tmpDir, "auth.json")
|
|
|
|
authOptions := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "testpass",
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
auth, err := CreateAuthenticator(authFile, authOptions)
|
|
require.NoError(t, err)
|
|
|
|
// Test invalid token
|
|
_, err = auth.VerifyAndDecodeJWT("invalid.token.here")
|
|
assert.Error(t, err, "Should return error for invalid token")
|
|
|
|
// Test empty token
|
|
_, err = auth.VerifyAndDecodeJWT("")
|
|
assert.Error(t, err, "Should return error for empty token")
|
|
}
|
|
|
|
func TestAuthenticator_VerifyAndDecodeJWT_ExpiredToken(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
authFile := filepath.Join(tmpDir, "auth.json")
|
|
|
|
authOptions := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "testpass",
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
auth, err := CreateAuthenticator(authFile, authOptions)
|
|
require.NoError(t, err)
|
|
|
|
payload := map[string]any{
|
|
"user": "testuser",
|
|
}
|
|
|
|
// Create token with 1 second expiry
|
|
token, err := auth.CreateJWT(payload, 1)
|
|
require.NoError(t, err)
|
|
|
|
// Wait for token to expire
|
|
time.Sleep(2 * time.Second)
|
|
|
|
// Try to verify expired token
|
|
_, err = auth.VerifyAndDecodeJWT(token)
|
|
assert.Error(t, err, "Should return error for expired token")
|
|
}
|
|
|
|
func TestAuthenticator_VerifyAndDecodeJWT_WrongKey(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
authFile1 := filepath.Join(tmpDir, "auth1.json")
|
|
authFile2 := filepath.Join(tmpDir, "auth2.json")
|
|
|
|
authOptions := &AuthOptions{
|
|
User: "testuser",
|
|
Pass: "testpass",
|
|
LockoutTime: 300,
|
|
LockoutLimit: 5,
|
|
}
|
|
|
|
// Create two different authenticators (different keys)
|
|
auth1, err := CreateAuthenticator(authFile1, authOptions)
|
|
require.NoError(t, err)
|
|
|
|
auth2, err := CreateAuthenticator(authFile2, authOptions)
|
|
require.NoError(t, err)
|
|
|
|
payload := map[string]any{
|
|
"user": "testuser",
|
|
}
|
|
|
|
// Create token with first authenticator
|
|
token, err := auth1.CreateJWT(payload)
|
|
require.NoError(t, err)
|
|
|
|
// Try to verify with second authenticator (different key)
|
|
_, err = auth2.VerifyAndDecodeJWT(token)
|
|
assert.Error(t, err, "Should return error when verifying token with wrong key")
|
|
}
|