mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-22 05:36:15 +01:00
Compare commits
2 Commits
adbfe7cfb7
...
be70840609
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be70840609 | ||
|
|
565162ef5f |
@@ -70,19 +70,11 @@ func TestNewWebSocketClient(t *testing.T) {
|
|||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// Set up environment
|
// Set up environment
|
||||||
if tc.hubURL != "" {
|
if tc.hubURL != "" {
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", tc.hubURL)
|
t.Setenv("BESZEL_AGENT_HUB_URL", tc.hubURL)
|
||||||
} else {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
}
|
}
|
||||||
if tc.token != "" {
|
if tc.token != "" {
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", tc.token)
|
t.Setenv("BESZEL_AGENT_TOKEN", tc.token)
|
||||||
} else {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
client, err := newWebSocketClient(agent)
|
client, err := newWebSocketClient(agent)
|
||||||
|
|
||||||
@@ -138,12 +130,8 @@ func TestWebSocketClient_GetOptions(t *testing.T) {
|
|||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// Set up environment
|
// Set up environment
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", tc.inputURL)
|
t.Setenv("BESZEL_AGENT_HUB_URL", tc.inputURL)
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
t.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
client, err := newWebSocketClient(agent)
|
client, err := newWebSocketClient(agent)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -185,12 +173,8 @@ func TestWebSocketClient_VerifySignature(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Set up environment
|
// Set up environment
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
t.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
t.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
client, err := newWebSocketClient(agent)
|
client, err := newWebSocketClient(agent)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -258,12 +242,8 @@ func TestWebSocketClient_HandleHubRequest(t *testing.T) {
|
|||||||
agent := createTestAgent(t)
|
agent := createTestAgent(t)
|
||||||
|
|
||||||
// Set up environment
|
// Set up environment
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
t.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
t.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
client, err := newWebSocketClient(agent)
|
client, err := newWebSocketClient(agent)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -350,13 +330,8 @@ func TestGetUserAgent(t *testing.T) {
|
|||||||
func TestWebSocketClient_Close(t *testing.T) {
|
func TestWebSocketClient_Close(t *testing.T) {
|
||||||
agent := createTestAgent(t)
|
agent := createTestAgent(t)
|
||||||
|
|
||||||
// Set up environment
|
t.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
t.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
client, err := newWebSocketClient(agent)
|
client, err := newWebSocketClient(agent)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -371,13 +346,8 @@ func TestWebSocketClient_Close(t *testing.T) {
|
|||||||
func TestWebSocketClient_ConnectRateLimit(t *testing.T) {
|
func TestWebSocketClient_ConnectRateLimit(t *testing.T) {
|
||||||
agent := createTestAgent(t)
|
agent := createTestAgent(t)
|
||||||
|
|
||||||
// Set up environment
|
t.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
t.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
client, err := newWebSocketClient(agent)
|
client, err := newWebSocketClient(agent)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -393,20 +363,10 @@ func TestWebSocketClient_ConnectRateLimit(t *testing.T) {
|
|||||||
|
|
||||||
// TestGetToken tests the getToken function with various scenarios
|
// TestGetToken tests the getToken function with various scenarios
|
||||||
func TestGetToken(t *testing.T) {
|
func TestGetToken(t *testing.T) {
|
||||||
unsetEnvVars := func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
os.Unsetenv("TOKEN")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN_FILE")
|
|
||||||
os.Unsetenv("TOKEN_FILE")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("token from TOKEN environment variable", func(t *testing.T) {
|
t.Run("token from TOKEN environment variable", func(t *testing.T) {
|
||||||
unsetEnvVars()
|
|
||||||
|
|
||||||
// Set TOKEN env var
|
// Set TOKEN env var
|
||||||
expectedToken := "test-token-from-env"
|
expectedToken := "test-token-from-env"
|
||||||
os.Setenv("TOKEN", expectedToken)
|
t.Setenv("TOKEN", expectedToken)
|
||||||
defer os.Unsetenv("TOKEN")
|
|
||||||
|
|
||||||
token, err := getToken()
|
token, err := getToken()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -414,12 +374,9 @@ func TestGetToken(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("token from BESZEL_AGENT_TOKEN environment variable", func(t *testing.T) {
|
t.Run("token from BESZEL_AGENT_TOKEN environment variable", func(t *testing.T) {
|
||||||
unsetEnvVars()
|
|
||||||
|
|
||||||
// Set BESZEL_AGENT_TOKEN env var (should take precedence)
|
// Set BESZEL_AGENT_TOKEN env var (should take precedence)
|
||||||
expectedToken := "test-token-from-beszel-env"
|
expectedToken := "test-token-from-beszel-env"
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", expectedToken)
|
t.Setenv("BESZEL_AGENT_TOKEN", expectedToken)
|
||||||
defer os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
|
|
||||||
token, err := getToken()
|
token, err := getToken()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -427,8 +384,6 @@ func TestGetToken(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("token from TOKEN_FILE", func(t *testing.T) {
|
t.Run("token from TOKEN_FILE", func(t *testing.T) {
|
||||||
unsetEnvVars()
|
|
||||||
|
|
||||||
// Create a temporary token file
|
// Create a temporary token file
|
||||||
expectedToken := "test-token-from-file"
|
expectedToken := "test-token-from-file"
|
||||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
||||||
@@ -440,8 +395,7 @@ func TestGetToken(t *testing.T) {
|
|||||||
tokenFile.Close()
|
tokenFile.Close()
|
||||||
|
|
||||||
// Set TOKEN_FILE env var
|
// Set TOKEN_FILE env var
|
||||||
os.Setenv("TOKEN_FILE", tokenFile.Name())
|
t.Setenv("TOKEN_FILE", tokenFile.Name())
|
||||||
defer os.Unsetenv("TOKEN_FILE")
|
|
||||||
|
|
||||||
token, err := getToken()
|
token, err := getToken()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -449,8 +403,6 @@ func TestGetToken(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("token from BESZEL_AGENT_TOKEN_FILE", func(t *testing.T) {
|
t.Run("token from BESZEL_AGENT_TOKEN_FILE", func(t *testing.T) {
|
||||||
unsetEnvVars()
|
|
||||||
|
|
||||||
// Create a temporary token file
|
// Create a temporary token file
|
||||||
expectedToken := "test-token-from-beszel-file"
|
expectedToken := "test-token-from-beszel-file"
|
||||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
||||||
@@ -462,8 +414,7 @@ func TestGetToken(t *testing.T) {
|
|||||||
tokenFile.Close()
|
tokenFile.Close()
|
||||||
|
|
||||||
// Set BESZEL_AGENT_TOKEN_FILE env var (should take precedence)
|
// Set BESZEL_AGENT_TOKEN_FILE env var (should take precedence)
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN_FILE", tokenFile.Name())
|
t.Setenv("BESZEL_AGENT_TOKEN_FILE", tokenFile.Name())
|
||||||
defer os.Unsetenv("BESZEL_AGENT_TOKEN_FILE")
|
|
||||||
|
|
||||||
token, err := getToken()
|
token, err := getToken()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -471,8 +422,6 @@ func TestGetToken(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("TOKEN takes precedence over TOKEN_FILE", func(t *testing.T) {
|
t.Run("TOKEN takes precedence over TOKEN_FILE", func(t *testing.T) {
|
||||||
unsetEnvVars()
|
|
||||||
|
|
||||||
// Create a temporary token file
|
// Create a temporary token file
|
||||||
fileToken := "token-from-file"
|
fileToken := "token-from-file"
|
||||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
||||||
@@ -485,12 +434,8 @@ func TestGetToken(t *testing.T) {
|
|||||||
|
|
||||||
// Set both TOKEN and TOKEN_FILE
|
// Set both TOKEN and TOKEN_FILE
|
||||||
envToken := "token-from-env"
|
envToken := "token-from-env"
|
||||||
os.Setenv("TOKEN", envToken)
|
t.Setenv("TOKEN", envToken)
|
||||||
os.Setenv("TOKEN_FILE", tokenFile.Name())
|
t.Setenv("TOKEN_FILE", tokenFile.Name())
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("TOKEN")
|
|
||||||
os.Unsetenv("TOKEN_FILE")
|
|
||||||
}()
|
|
||||||
|
|
||||||
token, err := getToken()
|
token, err := getToken()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -498,7 +443,10 @@ func TestGetToken(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("error when neither TOKEN nor TOKEN_FILE is set", func(t *testing.T) {
|
t.Run("error when neither TOKEN nor TOKEN_FILE is set", func(t *testing.T) {
|
||||||
unsetEnvVars()
|
t.Setenv("BESZEL_AGENT_TOKEN", "")
|
||||||
|
t.Setenv("TOKEN", "")
|
||||||
|
t.Setenv("BESZEL_AGENT_TOKEN_FILE", "")
|
||||||
|
t.Setenv("TOKEN_FILE", "")
|
||||||
|
|
||||||
token, err := getToken()
|
token, err := getToken()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
@@ -507,11 +455,8 @@ func TestGetToken(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("error when TOKEN_FILE points to non-existent file", func(t *testing.T) {
|
t.Run("error when TOKEN_FILE points to non-existent file", func(t *testing.T) {
|
||||||
unsetEnvVars()
|
|
||||||
|
|
||||||
// Set TOKEN_FILE to a non-existent file
|
// Set TOKEN_FILE to a non-existent file
|
||||||
os.Setenv("TOKEN_FILE", "/non/existent/file.txt")
|
t.Setenv("TOKEN_FILE", "/non/existent/file.txt")
|
||||||
defer os.Unsetenv("TOKEN_FILE")
|
|
||||||
|
|
||||||
token, err := getToken()
|
token, err := getToken()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
@@ -520,8 +465,6 @@ func TestGetToken(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("handles empty token file", func(t *testing.T) {
|
t.Run("handles empty token file", func(t *testing.T) {
|
||||||
unsetEnvVars()
|
|
||||||
|
|
||||||
// Create an empty token file
|
// Create an empty token file
|
||||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -529,8 +472,7 @@ func TestGetToken(t *testing.T) {
|
|||||||
tokenFile.Close()
|
tokenFile.Close()
|
||||||
|
|
||||||
// Set TOKEN_FILE env var
|
// Set TOKEN_FILE env var
|
||||||
os.Setenv("TOKEN_FILE", tokenFile.Name())
|
t.Setenv("TOKEN_FILE", tokenFile.Name())
|
||||||
defer os.Unsetenv("TOKEN_FILE")
|
|
||||||
|
|
||||||
token, err := getToken()
|
token, err := getToken()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -538,8 +480,6 @@ func TestGetToken(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("strips whitespace from TOKEN_FILE", func(t *testing.T) {
|
t.Run("strips whitespace from TOKEN_FILE", func(t *testing.T) {
|
||||||
unsetEnvVars()
|
|
||||||
|
|
||||||
tokenWithWhitespace := " test-token-with-whitespace \n\t"
|
tokenWithWhitespace := " test-token-with-whitespace \n\t"
|
||||||
expectedToken := "test-token-with-whitespace"
|
expectedToken := "test-token-with-whitespace"
|
||||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
||||||
@@ -550,8 +490,7 @@ func TestGetToken(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tokenFile.Close()
|
tokenFile.Close()
|
||||||
|
|
||||||
os.Setenv("TOKEN_FILE", tokenFile.Name())
|
t.Setenv("TOKEN_FILE", tokenFile.Name())
|
||||||
defer os.Unsetenv("TOKEN_FILE")
|
|
||||||
|
|
||||||
token, err := getToken()
|
token, err := getToken()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -183,10 +182,6 @@ func TestConnectionManager_TickerManagement(t *testing.T) {
|
|||||||
|
|
||||||
// TestConnectionManager_WebSocketConnectionFlow tests WebSocket connection logic
|
// TestConnectionManager_WebSocketConnectionFlow tests WebSocket connection logic
|
||||||
func TestConnectionManager_WebSocketConnectionFlow(t *testing.T) {
|
func TestConnectionManager_WebSocketConnectionFlow(t *testing.T) {
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("Skipping WebSocket connection test in short mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
agent := createTestAgent(t)
|
agent := createTestAgent(t)
|
||||||
cm := agent.connectionManager
|
cm := agent.connectionManager
|
||||||
|
|
||||||
@@ -196,19 +191,18 @@ func TestConnectionManager_WebSocketConnectionFlow(t *testing.T) {
|
|||||||
assert.Equal(t, Disconnected, cm.State, "State should remain Disconnected after failed connection")
|
assert.Equal(t, Disconnected, cm.State, "State should remain Disconnected after failed connection")
|
||||||
|
|
||||||
// Test with invalid URL
|
// Test with invalid URL
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", "invalid-url")
|
t.Setenv("BESZEL_AGENT_HUB_URL", "1,33%")
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
t.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Test with missing token
|
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
|
|
||||||
_, err2 := newWebSocketClient(agent)
|
_, err2 := newWebSocketClient(agent)
|
||||||
assert.Error(t, err2, "WebSocket client creation should fail without token")
|
assert.Error(t, err2, "WebSocket client creation should fail with invalid URL")
|
||||||
|
|
||||||
|
// Test with missing token
|
||||||
|
t.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
||||||
|
t.Setenv("BESZEL_AGENT_TOKEN", "")
|
||||||
|
|
||||||
|
_, err3 := newWebSocketClient(agent)
|
||||||
|
assert.Error(t, err3, "WebSocket client creation should fail without token")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestConnectionManager_ReconnectionLogic tests reconnection prevention logic
|
// TestConnectionManager_ReconnectionLogic tests reconnection prevention logic
|
||||||
@@ -234,12 +228,8 @@ func TestConnectionManager_ConnectWithRateLimit(t *testing.T) {
|
|||||||
cm := agent.connectionManager
|
cm := agent.connectionManager
|
||||||
|
|
||||||
// Set up environment for WebSocket client creation
|
// Set up environment for WebSocket client creation
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", "ws://localhost:8080")
|
t.Setenv("BESZEL_AGENT_HUB_URL", "ws://localhost:8080")
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
t.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Create WebSocket client
|
// Create WebSocket client
|
||||||
wsClient, err := newWebSocketClient(agent)
|
wsClient, err := newWebSocketClient(agent)
|
||||||
@@ -285,12 +275,8 @@ func TestConnectionManager_CloseWebSocket(t *testing.T) {
|
|||||||
}, "Should not panic when closing nil WebSocket client")
|
}, "Should not panic when closing nil WebSocket client")
|
||||||
|
|
||||||
// Set up environment and create WebSocket client
|
// Set up environment and create WebSocket client
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", "ws://localhost:8080")
|
t.Setenv("BESZEL_AGENT_HUB_URL", "ws://localhost:8080")
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
t.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
wsClient, err := newWebSocketClient(agent)
|
wsClient, err := newWebSocketClient(agent)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -39,17 +39,7 @@ func TestGetDataDir(t *testing.T) {
|
|||||||
t.Run("DATA_DIR environment variable", func(t *testing.T) {
|
t.Run("DATA_DIR environment variable", func(t *testing.T) {
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
// Set environment variable
|
t.Setenv("BESZEL_AGENT_DATA_DIR", tempDir)
|
||||||
oldValue := os.Getenv("DATA_DIR")
|
|
||||||
defer func() {
|
|
||||||
if oldValue == "" {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_DATA_DIR")
|
|
||||||
} else {
|
|
||||||
os.Setenv("BESZEL_AGENT_DATA_DIR", oldValue)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
os.Setenv("BESZEL_AGENT_DATA_DIR", tempDir)
|
|
||||||
|
|
||||||
result, err := GetDataDir()
|
result, err := GetDataDir()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -65,17 +55,6 @@ func TestGetDataDir(t *testing.T) {
|
|||||||
|
|
||||||
// Test fallback behavior (empty dataDir, no env var)
|
// Test fallback behavior (empty dataDir, no env var)
|
||||||
t.Run("fallback to default directories", func(t *testing.T) {
|
t.Run("fallback to default directories", func(t *testing.T) {
|
||||||
// Clear DATA_DIR environment variable
|
|
||||||
oldValue := os.Getenv("DATA_DIR")
|
|
||||||
defer func() {
|
|
||||||
if oldValue == "" {
|
|
||||||
os.Unsetenv("DATA_DIR")
|
|
||||||
} else {
|
|
||||||
os.Setenv("DATA_DIR", oldValue)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
os.Unsetenv("DATA_DIR")
|
|
||||||
|
|
||||||
// This will try platform-specific defaults, which may or may not work
|
// This will try platform-specific defaults, which may or may not work
|
||||||
// We're mainly testing that it doesn't panic and returns some result
|
// We're mainly testing that it doesn't panic and returns some result
|
||||||
result, err := GetDataDir()
|
result, err := GetDataDir()
|
||||||
|
|||||||
@@ -687,18 +687,8 @@ func TestIsDockerSpecialMountpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInitializeDiskInfoWithCustomNames(t *testing.T) {
|
func TestInitializeDiskInfoWithCustomNames(t *testing.T) {
|
||||||
// Set up environment variables
|
|
||||||
oldEnv := os.Getenv("EXTRA_FILESYSTEMS")
|
|
||||||
defer func() {
|
|
||||||
if oldEnv != "" {
|
|
||||||
os.Setenv("EXTRA_FILESYSTEMS", oldEnv)
|
|
||||||
} else {
|
|
||||||
os.Unsetenv("EXTRA_FILESYSTEMS")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Test with custom names
|
// Test with custom names
|
||||||
os.Setenv("EXTRA_FILESYSTEMS", "sda1__my-storage,/dev/sdb1__backup-drive,nvme0n1p2")
|
t.Setenv("EXTRA_FILESYSTEMS", "sda1__my-storage,/dev/sdb1__backup-drive,nvme0n1p2")
|
||||||
|
|
||||||
// Mock disk partitions (we'll just test the parsing logic)
|
// Mock disk partitions (we'll just test the parsing logic)
|
||||||
// Since the actual disk operations are system-dependent, we'll focus on the parsing
|
// Since the actual disk operations are system-dependent, we'll focus on the parsing
|
||||||
@@ -726,7 +716,7 @@ func TestInitializeDiskInfoWithCustomNames(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run("env_"+tc.envValue, func(t *testing.T) {
|
t.Run("env_"+tc.envValue, func(t *testing.T) {
|
||||||
os.Setenv("EXTRA_FILESYSTEMS", tc.envValue)
|
t.Setenv("EXTRA_FILESYSTEMS", tc.envValue)
|
||||||
|
|
||||||
// Create mock partitions that would match our test cases
|
// Create mock partitions that would match our test cases
|
||||||
partitions := []disk.PartitionStat{}
|
partitions := []disk.PartitionStat{}
|
||||||
|
|||||||
@@ -1083,8 +1083,6 @@ func TestCalculateGPUAverage(t *testing.T) {
|
|||||||
|
|
||||||
func TestGPUCapabilitiesAndLegacyPriority(t *testing.T) {
|
func TestGPUCapabilitiesAndLegacyPriority(t *testing.T) {
|
||||||
// Save original PATH
|
// Save original PATH
|
||||||
origPath := os.Getenv("PATH")
|
|
||||||
defer os.Setenv("PATH", origPath)
|
|
||||||
hasAmdSysfs := (&GPUManager{}).hasAmdSysfs()
|
hasAmdSysfs := (&GPUManager{}).hasAmdSysfs()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -1178,7 +1176,7 @@ echo "[]"`
|
|||||||
{
|
{
|
||||||
name: "no gpu tools available",
|
name: "no gpu tools available",
|
||||||
setupCommands: func(_ string) error {
|
setupCommands: func(_ string) error {
|
||||||
os.Setenv("PATH", "")
|
t.Setenv("PATH", "")
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@@ -1188,7 +1186,7 @@ echo "[]"`
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
os.Setenv("PATH", tempDir)
|
t.Setenv("PATH", tempDir)
|
||||||
if err := tt.setupCommands(tempDir); err != nil {
|
if err := tt.setupCommands(tempDir); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1234,13 +1232,9 @@ echo "[]"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCollectorStartHelpers(t *testing.T) {
|
func TestCollectorStartHelpers(t *testing.T) {
|
||||||
// Save original PATH
|
|
||||||
origPath := os.Getenv("PATH")
|
|
||||||
defer os.Setenv("PATH", origPath)
|
|
||||||
|
|
||||||
// Set up temp dir with the commands
|
// Set up temp dir with the commands
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
os.Setenv("PATH", dir)
|
t.Setenv("PATH", dir)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -1370,11 +1364,8 @@ echo '[{"device_name":"NVIDIA Test GPU","temp":"52C","power_draw":"31W","gpu_uti
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewGPUManagerPriorityNvtopFallback(t *testing.T) {
|
func TestNewGPUManagerPriorityNvtopFallback(t *testing.T) {
|
||||||
origPath := os.Getenv("PATH")
|
|
||||||
defer os.Setenv("PATH", origPath)
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
os.Setenv("PATH", dir)
|
t.Setenv("PATH", dir)
|
||||||
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "nvtop,nvidia-smi")
|
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "nvtop,nvidia-smi")
|
||||||
|
|
||||||
nvtopPath := filepath.Join(dir, "nvtop")
|
nvtopPath := filepath.Join(dir, "nvtop")
|
||||||
@@ -1399,11 +1390,8 @@ echo "0, NVIDIA Priority GPU, 45, 512, 2048, 12, 25"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewGPUManagerPriorityMixedCollectors(t *testing.T) {
|
func TestNewGPUManagerPriorityMixedCollectors(t *testing.T) {
|
||||||
origPath := os.Getenv("PATH")
|
|
||||||
defer os.Setenv("PATH", origPath)
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
os.Setenv("PATH", dir)
|
t.Setenv("PATH", dir)
|
||||||
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "intel_gpu_top,rocm-smi")
|
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "intel_gpu_top,rocm-smi")
|
||||||
|
|
||||||
intelPath := filepath.Join(dir, "intel_gpu_top")
|
intelPath := filepath.Join(dir, "intel_gpu_top")
|
||||||
@@ -1433,11 +1421,8 @@ echo '{"card0": {"Temperature (Sensor edge) (C)": "49.0", "Current Socket Graphi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewGPUManagerPriorityNvmlFallbackToNvidiaSmi(t *testing.T) {
|
func TestNewGPUManagerPriorityNvmlFallbackToNvidiaSmi(t *testing.T) {
|
||||||
origPath := os.Getenv("PATH")
|
|
||||||
defer os.Setenv("PATH", origPath)
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
os.Setenv("PATH", dir)
|
t.Setenv("PATH", dir)
|
||||||
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "nvml,nvidia-smi")
|
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "nvml,nvidia-smi")
|
||||||
|
|
||||||
nvidiaPath := filepath.Join(dir, "nvidia-smi")
|
nvidiaPath := filepath.Join(dir, "nvidia-smi")
|
||||||
@@ -1456,11 +1441,8 @@ echo "0, NVIDIA Fallback GPU, 41, 256, 1024, 8, 14"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewGPUManagerConfiguredCollectorsMustStart(t *testing.T) {
|
func TestNewGPUManagerConfiguredCollectorsMustStart(t *testing.T) {
|
||||||
origPath := os.Getenv("PATH")
|
|
||||||
defer os.Setenv("PATH", origPath)
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
os.Setenv("PATH", dir)
|
t.Setenv("PATH", dir)
|
||||||
|
|
||||||
t.Run("configured valid collector unavailable", func(t *testing.T) {
|
t.Run("configured valid collector unavailable", func(t *testing.T) {
|
||||||
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "nvidia-smi")
|
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "nvidia-smi")
|
||||||
@@ -1480,11 +1462,8 @@ func TestNewGPUManagerConfiguredCollectorsMustStart(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewGPUManagerJetsonIgnoresCollectorConfig(t *testing.T) {
|
func TestNewGPUManagerJetsonIgnoresCollectorConfig(t *testing.T) {
|
||||||
origPath := os.Getenv("PATH")
|
|
||||||
defer os.Setenv("PATH", origPath)
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
os.Setenv("PATH", dir)
|
t.Setenv("PATH", dir)
|
||||||
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "nvidia-smi")
|
t.Setenv("BESZEL_AGENT_GPU_COLLECTOR", "nvidia-smi")
|
||||||
|
|
||||||
tegraPath := filepath.Join(dir, "tegrastats")
|
tegraPath := filepath.Join(dir, "tegrastats")
|
||||||
@@ -1719,12 +1698,8 @@ func TestIntelUpdateFromStats(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIntelCollectorStreaming(t *testing.T) {
|
func TestIntelCollectorStreaming(t *testing.T) {
|
||||||
// Save and override PATH
|
|
||||||
origPath := os.Getenv("PATH")
|
|
||||||
defer os.Setenv("PATH", origPath)
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
os.Setenv("PATH", dir)
|
t.Setenv("PATH", dir)
|
||||||
|
|
||||||
// Create a fake intel_gpu_top that prints -l format with four samples (first will be skipped) and exits
|
// Create a fake intel_gpu_top that prints -l format with four samples (first will be skipped) and exits
|
||||||
scriptPath := filepath.Join(dir, "intel_gpu_top")
|
scriptPath := filepath.Join(dir, "intel_gpu_top")
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/henrygd/beszel/internal/entities/system"
|
"github.com/henrygd/beszel/internal/entities/system"
|
||||||
@@ -329,34 +328,10 @@ func TestNewSensorConfigWithEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewSensorConfig(t *testing.T) {
|
func TestNewSensorConfig(t *testing.T) {
|
||||||
// Save original environment variables
|
|
||||||
originalPrimary, hasPrimary := os.LookupEnv("BESZEL_AGENT_PRIMARY_SENSOR")
|
|
||||||
originalSys, hasSys := os.LookupEnv("BESZEL_AGENT_SYS_SENSORS")
|
|
||||||
originalSensors, hasSensors := os.LookupEnv("BESZEL_AGENT_SENSORS")
|
|
||||||
|
|
||||||
// Restore environment variables after the test
|
|
||||||
defer func() {
|
|
||||||
// Clean up test environment variables
|
|
||||||
os.Unsetenv("BESZEL_AGENT_PRIMARY_SENSOR")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_SYS_SENSORS")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_SENSORS")
|
|
||||||
|
|
||||||
// Restore original values if they existed
|
|
||||||
if hasPrimary {
|
|
||||||
os.Setenv("BESZEL_AGENT_PRIMARY_SENSOR", originalPrimary)
|
|
||||||
}
|
|
||||||
if hasSys {
|
|
||||||
os.Setenv("BESZEL_AGENT_SYS_SENSORS", originalSys)
|
|
||||||
}
|
|
||||||
if hasSensors {
|
|
||||||
os.Setenv("BESZEL_AGENT_SENSORS", originalSensors)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Set test environment variables
|
// Set test environment variables
|
||||||
os.Setenv("BESZEL_AGENT_PRIMARY_SENSOR", "test_primary")
|
t.Setenv("BESZEL_AGENT_PRIMARY_SENSOR", "test_primary")
|
||||||
os.Setenv("BESZEL_AGENT_SYS_SENSORS", "/test/path")
|
t.Setenv("BESZEL_AGENT_SYS_SENSORS", "/test/path")
|
||||||
os.Setenv("BESZEL_AGENT_SENSORS", "test_sensor1,test_*,test_sensor3")
|
t.Setenv("BESZEL_AGENT_SENSORS", "test_sensor1,test_*,test_sensor3")
|
||||||
|
|
||||||
agent := &Agent{}
|
agent := &Agent{}
|
||||||
result := agent.newSensorConfig()
|
result := agent.newSensorConfig()
|
||||||
|
|||||||
@@ -183,8 +183,7 @@ func TestStartServer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStartServerDisableSSH(t *testing.T) {
|
func TestStartServerDisableSSH(t *testing.T) {
|
||||||
os.Setenv("BESZEL_AGENT_DISABLE_SSH", "true")
|
t.Setenv("BESZEL_AGENT_DISABLE_SSH", "true")
|
||||||
defer os.Unsetenv("BESZEL_AGENT_DISABLE_SSH")
|
|
||||||
|
|
||||||
agent, err := NewAgent("")
|
agent, err := NewAgent("")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -1035,7 +1035,7 @@ func TestRefreshExcludedDevices(t *testing.T) {
|
|||||||
t.Setenv("EXCLUDE_SMART", tt.envValue)
|
t.Setenv("EXCLUDE_SMART", tt.envValue)
|
||||||
} else {
|
} else {
|
||||||
// Ensure env var is not set for empty test
|
// Ensure env var is not set for empty test
|
||||||
os.Unsetenv("EXCLUDE_SMART")
|
t.Setenv("EXCLUDE_SMART", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
sm := &SmartManager{}
|
sm := &SmartManager{}
|
||||||
|
|||||||
@@ -167,16 +167,12 @@ func TestGetServicePatterns(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// Clean up any existing env vars
|
|
||||||
os.Unsetenv("BESZEL_AGENT_SERVICE_PATTERNS")
|
|
||||||
os.Unsetenv("SERVICE_PATTERNS")
|
|
||||||
|
|
||||||
// Set up environment variables
|
// Set up environment variables
|
||||||
if tt.prefixedEnv != "" {
|
if tt.prefixedEnv != "" {
|
||||||
os.Setenv("BESZEL_AGENT_SERVICE_PATTERNS", tt.prefixedEnv)
|
t.Setenv("BESZEL_AGENT_SERVICE_PATTERNS", tt.prefixedEnv)
|
||||||
}
|
}
|
||||||
if tt.unprefixedEnv != "" {
|
if tt.unprefixedEnv != "" {
|
||||||
os.Setenv("SERVICE_PATTERNS", tt.unprefixedEnv)
|
t.Setenv("SERVICE_PATTERNS", tt.unprefixedEnv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the function
|
// Run the function
|
||||||
@@ -184,12 +180,6 @@ func TestGetServicePatterns(t *testing.T) {
|
|||||||
|
|
||||||
// Verify results
|
// Verify results
|
||||||
assert.Equal(t, tt.expected, result, "Patterns should match expected values")
|
assert.Equal(t, tt.expected, result, "Patterns should match expected values")
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
if tt.cleanupEnvVars {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_SERVICE_PATTERNS")
|
|
||||||
os.Unsetenv("SERVICE_PATTERNS")
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,10 +134,8 @@ func TestGetEnv(t *testing.T) {
|
|||||||
prefixedKey := "BESZEL_AGENT_" + key
|
prefixedKey := "BESZEL_AGENT_" + key
|
||||||
|
|
||||||
t.Run("prefixed variable exists", func(t *testing.T) {
|
t.Run("prefixed variable exists", func(t *testing.T) {
|
||||||
os.Setenv(prefixedKey, "prefixed_val")
|
t.Setenv(prefixedKey, "prefixed_val")
|
||||||
os.Setenv(key, "unprefixed_val")
|
t.Setenv(key, "unprefixed_val")
|
||||||
defer os.Unsetenv(prefixedKey)
|
|
||||||
defer os.Unsetenv(key)
|
|
||||||
|
|
||||||
val, exists := GetEnv(key)
|
val, exists := GetEnv(key)
|
||||||
assert.True(t, exists)
|
assert.True(t, exists)
|
||||||
@@ -145,9 +143,7 @@ func TestGetEnv(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("only unprefixed variable exists", func(t *testing.T) {
|
t.Run("only unprefixed variable exists", func(t *testing.T) {
|
||||||
os.Unsetenv(prefixedKey)
|
t.Setenv(key, "unprefixed_val")
|
||||||
os.Setenv(key, "unprefixed_val")
|
|
||||||
defer os.Unsetenv(key)
|
|
||||||
|
|
||||||
val, exists := GetEnv(key)
|
val, exists := GetEnv(key)
|
||||||
assert.True(t, exists)
|
assert.True(t, exists)
|
||||||
@@ -155,9 +151,6 @@ func TestGetEnv(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("neither variable exists", func(t *testing.T) {
|
t.Run("neither variable exists", func(t *testing.T) {
|
||||||
os.Unsetenv(prefixedKey)
|
|
||||||
os.Unsetenv(key)
|
|
||||||
|
|
||||||
val, exists := GetEnv(key)
|
val, exists := GetEnv(key)
|
||||||
assert.False(t, exists)
|
assert.False(t, exists)
|
||||||
assert.Empty(t, val)
|
assert.Empty(t, val)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
baseApp := getBaseApp()
|
baseApp := getBaseApp()
|
||||||
h := hub.NewHub(baseApp)
|
h, _ := hub.NewHub(baseApp)
|
||||||
if err := h.StartHub(); err != nil {
|
if err := h.StartHub(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ func createTestHub(t testing.TB) (*Hub, *pbtests.TestApp, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return NewHub(testApp), testApp, nil
|
h, err := NewHub(testApp)
|
||||||
|
return h, testApp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanupTestHub stops background system goroutines before tearing down the app.
|
// cleanupTestHub stops background system goroutines before tearing down the app.
|
||||||
@@ -897,12 +898,8 @@ func TestAgentWebSocketIntegration(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Set up environment variables for the agent
|
// Set up environment variables for the agent
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", ts.URL)
|
t.Setenv("BESZEL_AGENT_HUB_URL", ts.URL)
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", tc.agentToken)
|
t.Setenv("BESZEL_AGENT_TOKEN", tc.agentToken)
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Start agent in background
|
// Start agent in background
|
||||||
done := make(chan error, 1)
|
done := make(chan error, 1)
|
||||||
@@ -1080,12 +1077,8 @@ func TestMultipleSystemsWithSameUniversalToken(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Set up environment variables for the agent
|
// Set up environment variables for the agent
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", ts.URL)
|
t.Setenv("BESZEL_AGENT_HUB_URL", ts.URL)
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", universalToken)
|
t.Setenv("BESZEL_AGENT_TOKEN", universalToken)
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Count systems before connection
|
// Count systems before connection
|
||||||
systemsBefore, err := testApp.FindRecordsByFilter("systems", "users ~ {:userId}", "", -1, 0, map[string]any{"userId": userRecord.Id})
|
systemsBefore, err := testApp.FindRecordsByFilter("systems", "users ~ {:userId}", "", -1, 0, map[string]any{"userId": userRecord.Id})
|
||||||
@@ -1243,12 +1236,8 @@ func TestPermanentUniversalTokenFromDB(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Set up environment variables for the agent
|
// Set up environment variables for the agent
|
||||||
os.Setenv("BESZEL_AGENT_HUB_URL", ts.URL)
|
t.Setenv("BESZEL_AGENT_HUB_URL", ts.URL)
|
||||||
os.Setenv("BESZEL_AGENT_TOKEN", universalToken)
|
t.Setenv("BESZEL_AGENT_TOKEN", universalToken)
|
||||||
defer func() {
|
|
||||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
|
||||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Start agent in background
|
// Start agent in background
|
||||||
done := make(chan error, 1)
|
done := make(chan error, 1)
|
||||||
|
|||||||
128
internal/hub/collections.go
Normal file
128
internal/hub/collections.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package hub
|
||||||
|
|
||||||
|
import "github.com/pocketbase/pocketbase/core"
|
||||||
|
|
||||||
|
type collectionRules struct {
|
||||||
|
list *string
|
||||||
|
view *string
|
||||||
|
create *string
|
||||||
|
update *string
|
||||||
|
delete *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCollectionAuthSettings applies Beszel's collection auth settings.
|
||||||
|
func setCollectionAuthSettings(app core.App) error {
|
||||||
|
usersCollection, err := app.FindCollectionByNameOrId("users")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
superusersCollection, err := app.FindCollectionByNameOrId(core.CollectionNameSuperusers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable email auth if DISABLE_PASSWORD_AUTH env var is set
|
||||||
|
disablePasswordAuth, _ := GetEnv("DISABLE_PASSWORD_AUTH")
|
||||||
|
usersCollection.PasswordAuth.Enabled = disablePasswordAuth != "true"
|
||||||
|
usersCollection.PasswordAuth.IdentityFields = []string{"email"}
|
||||||
|
// allow oauth user creation if USER_CREATION is set
|
||||||
|
if userCreation, _ := GetEnv("USER_CREATION"); userCreation == "true" {
|
||||||
|
cr := "@request.context = 'oauth2'"
|
||||||
|
usersCollection.CreateRule = &cr
|
||||||
|
} else {
|
||||||
|
usersCollection.CreateRule = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable mfaOtp mfa if MFA_OTP env var is set
|
||||||
|
mfaOtp, _ := GetEnv("MFA_OTP")
|
||||||
|
usersCollection.OTP.Length = 6
|
||||||
|
superusersCollection.OTP.Length = 6
|
||||||
|
usersCollection.OTP.Enabled = mfaOtp == "true"
|
||||||
|
usersCollection.MFA.Enabled = mfaOtp == "true"
|
||||||
|
superusersCollection.OTP.Enabled = mfaOtp == "true" || mfaOtp == "superusers"
|
||||||
|
superusersCollection.MFA.Enabled = mfaOtp == "true" || mfaOtp == "superusers"
|
||||||
|
if err := app.Save(superusersCollection); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := app.Save(usersCollection); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// When SHARE_ALL_SYSTEMS is enabled, any authenticated user can read
|
||||||
|
// system-scoped data. Write rules continue to block readonly users.
|
||||||
|
shareAllSystems, _ := GetEnv("SHARE_ALL_SYSTEMS")
|
||||||
|
|
||||||
|
authenticatedRule := "@request.auth.id != \"\""
|
||||||
|
systemsMemberRule := authenticatedRule + " && users.id ?= @request.auth.id"
|
||||||
|
systemMemberRule := authenticatedRule + " && system.users.id ?= @request.auth.id"
|
||||||
|
|
||||||
|
systemsReadRule := systemsMemberRule
|
||||||
|
systemScopedReadRule := systemMemberRule
|
||||||
|
if shareAllSystems == "true" {
|
||||||
|
systemsReadRule = authenticatedRule
|
||||||
|
systemScopedReadRule = authenticatedRule
|
||||||
|
}
|
||||||
|
systemsWriteRule := systemsReadRule + " && @request.auth.role != \"readonly\""
|
||||||
|
systemScopedWriteRule := systemScopedReadRule + " && @request.auth.role != \"readonly\""
|
||||||
|
|
||||||
|
if err := applyCollectionRules(app, []string{"systems"}, collectionRules{
|
||||||
|
list: &systemsReadRule,
|
||||||
|
view: &systemsReadRule,
|
||||||
|
create: &systemsWriteRule,
|
||||||
|
update: &systemsWriteRule,
|
||||||
|
delete: &systemsWriteRule,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := applyCollectionRules(app, []string{"containers", "container_stats", "system_stats", "systemd_services"}, collectionRules{
|
||||||
|
list: &systemScopedReadRule,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := applyCollectionRules(app, []string{"smart_devices"}, collectionRules{
|
||||||
|
list: &systemScopedReadRule,
|
||||||
|
view: &systemScopedReadRule,
|
||||||
|
delete: &systemScopedWriteRule,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := applyCollectionRules(app, []string{"fingerprints"}, collectionRules{
|
||||||
|
list: &systemScopedReadRule,
|
||||||
|
view: &systemScopedReadRule,
|
||||||
|
create: &systemScopedWriteRule,
|
||||||
|
update: &systemScopedWriteRule,
|
||||||
|
delete: &systemScopedWriteRule,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := applyCollectionRules(app, []string{"system_details"}, collectionRules{
|
||||||
|
list: &systemScopedReadRule,
|
||||||
|
view: &systemScopedReadRule,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyCollectionRules(app core.App, collectionNames []string, rules collectionRules) error {
|
||||||
|
for _, collectionName := range collectionNames {
|
||||||
|
collection, err := app.FindCollectionByNameOrId(collectionName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
collection.ListRule = rules.list
|
||||||
|
collection.ViewRule = rules.view
|
||||||
|
collection.CreateRule = rules.create
|
||||||
|
collection.UpdateRule = rules.update
|
||||||
|
collection.DeleteRule = rules.delete
|
||||||
|
if err := app.Save(collection); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
527
internal/hub/collections_test.go
Normal file
527
internal/hub/collections_test.go
Normal file
@@ -0,0 +1,527 @@
|
|||||||
|
//go:build testing
|
||||||
|
|
||||||
|
package hub_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
beszelTests "github.com/henrygd/beszel/internal/tests"
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
pbTests "github.com/pocketbase/pocketbase/tests"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCollectionRulesDefault(t *testing.T) {
|
||||||
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
|
defer hub.Cleanup()
|
||||||
|
|
||||||
|
const isUserMatchesUser = `@request.auth.id != "" && user = @request.auth.id`
|
||||||
|
|
||||||
|
const isUserInUsers = `@request.auth.id != "" && users.id ?= @request.auth.id`
|
||||||
|
const isUserInUsersNotReadonly = `@request.auth.id != "" && users.id ?= @request.auth.id && @request.auth.role != "readonly"`
|
||||||
|
|
||||||
|
const isUserInSystemUsers = `@request.auth.id != "" && system.users.id ?= @request.auth.id`
|
||||||
|
const isUserInSystemUsersNotReadonly = `@request.auth.id != "" && system.users.id ?= @request.auth.id && @request.auth.role != "readonly"`
|
||||||
|
|
||||||
|
// users collection
|
||||||
|
usersCollection, err := hub.FindCollectionByNameOrId("users")
|
||||||
|
assert.NoError(t, err, "Failed to find users collection")
|
||||||
|
assert.True(t, usersCollection.PasswordAuth.Enabled)
|
||||||
|
assert.Equal(t, usersCollection.PasswordAuth.IdentityFields, []string{"email"})
|
||||||
|
assert.Nil(t, usersCollection.CreateRule)
|
||||||
|
assert.False(t, usersCollection.MFA.Enabled)
|
||||||
|
|
||||||
|
// superusers collection
|
||||||
|
superusersCollection, err := hub.FindCollectionByNameOrId(core.CollectionNameSuperusers)
|
||||||
|
assert.NoError(t, err, "Failed to find superusers collection")
|
||||||
|
assert.True(t, superusersCollection.PasswordAuth.Enabled)
|
||||||
|
assert.Equal(t, superusersCollection.PasswordAuth.IdentityFields, []string{"email"})
|
||||||
|
assert.Nil(t, superusersCollection.CreateRule)
|
||||||
|
assert.False(t, superusersCollection.MFA.Enabled)
|
||||||
|
|
||||||
|
// alerts collection
|
||||||
|
alertsCollection, err := hub.FindCollectionByNameOrId("alerts")
|
||||||
|
require.NoError(t, err, "Failed to find alerts collection")
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsCollection.ListRule)
|
||||||
|
assert.Nil(t, alertsCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// alerts_history collection
|
||||||
|
alertsHistoryCollection, err := hub.FindCollectionByNameOrId("alerts_history")
|
||||||
|
require.NoError(t, err, "Failed to find alerts_history collection")
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsHistoryCollection.ListRule)
|
||||||
|
assert.Nil(t, alertsHistoryCollection.ViewRule)
|
||||||
|
assert.Nil(t, alertsHistoryCollection.CreateRule)
|
||||||
|
assert.Nil(t, alertsHistoryCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsHistoryCollection.DeleteRule)
|
||||||
|
|
||||||
|
// containers collection
|
||||||
|
containersCollection, err := hub.FindCollectionByNameOrId("containers")
|
||||||
|
require.NoError(t, err, "Failed to find containers collection")
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *containersCollection.ListRule)
|
||||||
|
assert.Nil(t, containersCollection.ViewRule)
|
||||||
|
assert.Nil(t, containersCollection.CreateRule)
|
||||||
|
assert.Nil(t, containersCollection.UpdateRule)
|
||||||
|
assert.Nil(t, containersCollection.DeleteRule)
|
||||||
|
|
||||||
|
// container_stats collection
|
||||||
|
containerStatsCollection, err := hub.FindCollectionByNameOrId("container_stats")
|
||||||
|
require.NoError(t, err, "Failed to find container_stats collection")
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *containerStatsCollection.ListRule)
|
||||||
|
assert.Nil(t, containerStatsCollection.ViewRule)
|
||||||
|
assert.Nil(t, containerStatsCollection.CreateRule)
|
||||||
|
assert.Nil(t, containerStatsCollection.UpdateRule)
|
||||||
|
assert.Nil(t, containerStatsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// fingerprints collection
|
||||||
|
fingerprintsCollection, err := hub.FindCollectionByNameOrId("fingerprints")
|
||||||
|
require.NoError(t, err, "Failed to find fingerprints collection")
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *fingerprintsCollection.ListRule)
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *fingerprintsCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserInSystemUsersNotReadonly, *fingerprintsCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserInSystemUsersNotReadonly, *fingerprintsCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserInSystemUsersNotReadonly, *fingerprintsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// quiet_hours collection
|
||||||
|
quietHoursCollection, err := hub.FindCollectionByNameOrId("quiet_hours")
|
||||||
|
require.NoError(t, err, "Failed to find quiet_hours collection")
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.ListRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.DeleteRule)
|
||||||
|
|
||||||
|
// smart_devices collection
|
||||||
|
smartDevicesCollection, err := hub.FindCollectionByNameOrId("smart_devices")
|
||||||
|
require.NoError(t, err, "Failed to find smart_devices collection")
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *smartDevicesCollection.ListRule)
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *smartDevicesCollection.ViewRule)
|
||||||
|
assert.Nil(t, smartDevicesCollection.CreateRule)
|
||||||
|
assert.Nil(t, smartDevicesCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserInSystemUsersNotReadonly, *smartDevicesCollection.DeleteRule)
|
||||||
|
|
||||||
|
// system_details collection
|
||||||
|
systemDetailsCollection, err := hub.FindCollectionByNameOrId("system_details")
|
||||||
|
require.NoError(t, err, "Failed to find system_details collection")
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *systemDetailsCollection.ListRule)
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *systemDetailsCollection.ViewRule)
|
||||||
|
assert.Nil(t, systemDetailsCollection.CreateRule)
|
||||||
|
assert.Nil(t, systemDetailsCollection.UpdateRule)
|
||||||
|
assert.Nil(t, systemDetailsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// system_stats collection
|
||||||
|
systemStatsCollection, err := hub.FindCollectionByNameOrId("system_stats")
|
||||||
|
require.NoError(t, err, "Failed to find system_stats collection")
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *systemStatsCollection.ListRule)
|
||||||
|
assert.Nil(t, systemStatsCollection.ViewRule)
|
||||||
|
assert.Nil(t, systemStatsCollection.CreateRule)
|
||||||
|
assert.Nil(t, systemStatsCollection.UpdateRule)
|
||||||
|
assert.Nil(t, systemStatsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// systemd_services collection
|
||||||
|
systemdServicesCollection, err := hub.FindCollectionByNameOrId("systemd_services")
|
||||||
|
require.NoError(t, err, "Failed to find systemd_services collection")
|
||||||
|
assert.Equal(t, isUserInSystemUsers, *systemdServicesCollection.ListRule)
|
||||||
|
assert.Nil(t, systemdServicesCollection.ViewRule)
|
||||||
|
assert.Nil(t, systemdServicesCollection.CreateRule)
|
||||||
|
assert.Nil(t, systemdServicesCollection.UpdateRule)
|
||||||
|
assert.Nil(t, systemdServicesCollection.DeleteRule)
|
||||||
|
|
||||||
|
// systems collection
|
||||||
|
systemsCollection, err := hub.FindCollectionByNameOrId("systems")
|
||||||
|
require.NoError(t, err, "Failed to find systems collection")
|
||||||
|
assert.Equal(t, isUserInUsers, *systemsCollection.ListRule)
|
||||||
|
assert.Equal(t, isUserInUsers, *systemsCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserInUsersNotReadonly, *systemsCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserInUsersNotReadonly, *systemsCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserInUsersNotReadonly, *systemsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// universal_tokens collection
|
||||||
|
universalTokensCollection, err := hub.FindCollectionByNameOrId("universal_tokens")
|
||||||
|
require.NoError(t, err, "Failed to find universal_tokens collection")
|
||||||
|
assert.Nil(t, universalTokensCollection.ListRule)
|
||||||
|
assert.Nil(t, universalTokensCollection.ViewRule)
|
||||||
|
assert.Nil(t, universalTokensCollection.CreateRule)
|
||||||
|
assert.Nil(t, universalTokensCollection.UpdateRule)
|
||||||
|
assert.Nil(t, universalTokensCollection.DeleteRule)
|
||||||
|
|
||||||
|
// user_settings collection
|
||||||
|
userSettingsCollection, err := hub.FindCollectionByNameOrId("user_settings")
|
||||||
|
require.NoError(t, err, "Failed to find user_settings collection")
|
||||||
|
assert.Equal(t, isUserMatchesUser, *userSettingsCollection.ListRule)
|
||||||
|
assert.Nil(t, userSettingsCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *userSettingsCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *userSettingsCollection.UpdateRule)
|
||||||
|
assert.Nil(t, userSettingsCollection.DeleteRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCollectionRulesShareAllSystems(t *testing.T) {
|
||||||
|
t.Setenv("SHARE_ALL_SYSTEMS", "true")
|
||||||
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
|
defer hub.Cleanup()
|
||||||
|
|
||||||
|
const isUser = `@request.auth.id != ""`
|
||||||
|
const isUserNotReadonly = `@request.auth.id != "" && @request.auth.role != "readonly"`
|
||||||
|
|
||||||
|
const isUserMatchesUser = `@request.auth.id != "" && user = @request.auth.id`
|
||||||
|
|
||||||
|
// alerts collection
|
||||||
|
alertsCollection, err := hub.FindCollectionByNameOrId("alerts")
|
||||||
|
require.NoError(t, err, "Failed to find alerts collection")
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsCollection.ListRule)
|
||||||
|
assert.Nil(t, alertsCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// alerts_history collection
|
||||||
|
alertsHistoryCollection, err := hub.FindCollectionByNameOrId("alerts_history")
|
||||||
|
require.NoError(t, err, "Failed to find alerts_history collection")
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsHistoryCollection.ListRule)
|
||||||
|
assert.Nil(t, alertsHistoryCollection.ViewRule)
|
||||||
|
assert.Nil(t, alertsHistoryCollection.CreateRule)
|
||||||
|
assert.Nil(t, alertsHistoryCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *alertsHistoryCollection.DeleteRule)
|
||||||
|
|
||||||
|
// containers collection
|
||||||
|
containersCollection, err := hub.FindCollectionByNameOrId("containers")
|
||||||
|
require.NoError(t, err, "Failed to find containers collection")
|
||||||
|
assert.Equal(t, isUser, *containersCollection.ListRule)
|
||||||
|
assert.Nil(t, containersCollection.ViewRule)
|
||||||
|
assert.Nil(t, containersCollection.CreateRule)
|
||||||
|
assert.Nil(t, containersCollection.UpdateRule)
|
||||||
|
assert.Nil(t, containersCollection.DeleteRule)
|
||||||
|
|
||||||
|
// container_stats collection
|
||||||
|
containerStatsCollection, err := hub.FindCollectionByNameOrId("container_stats")
|
||||||
|
require.NoError(t, err, "Failed to find container_stats collection")
|
||||||
|
assert.Equal(t, isUser, *containerStatsCollection.ListRule)
|
||||||
|
assert.Nil(t, containerStatsCollection.ViewRule)
|
||||||
|
assert.Nil(t, containerStatsCollection.CreateRule)
|
||||||
|
assert.Nil(t, containerStatsCollection.UpdateRule)
|
||||||
|
assert.Nil(t, containerStatsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// fingerprints collection
|
||||||
|
fingerprintsCollection, err := hub.FindCollectionByNameOrId("fingerprints")
|
||||||
|
require.NoError(t, err, "Failed to find fingerprints collection")
|
||||||
|
assert.Equal(t, isUser, *fingerprintsCollection.ListRule)
|
||||||
|
assert.Equal(t, isUser, *fingerprintsCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserNotReadonly, *fingerprintsCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserNotReadonly, *fingerprintsCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserNotReadonly, *fingerprintsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// quiet_hours collection
|
||||||
|
quietHoursCollection, err := hub.FindCollectionByNameOrId("quiet_hours")
|
||||||
|
require.NoError(t, err, "Failed to find quiet_hours collection")
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.ListRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *quietHoursCollection.DeleteRule)
|
||||||
|
|
||||||
|
// smart_devices collection
|
||||||
|
smartDevicesCollection, err := hub.FindCollectionByNameOrId("smart_devices")
|
||||||
|
require.NoError(t, err, "Failed to find smart_devices collection")
|
||||||
|
assert.Equal(t, isUser, *smartDevicesCollection.ListRule)
|
||||||
|
assert.Equal(t, isUser, *smartDevicesCollection.ViewRule)
|
||||||
|
assert.Nil(t, smartDevicesCollection.CreateRule)
|
||||||
|
assert.Nil(t, smartDevicesCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserNotReadonly, *smartDevicesCollection.DeleteRule)
|
||||||
|
|
||||||
|
// system_details collection
|
||||||
|
systemDetailsCollection, err := hub.FindCollectionByNameOrId("system_details")
|
||||||
|
require.NoError(t, err, "Failed to find system_details collection")
|
||||||
|
assert.Equal(t, isUser, *systemDetailsCollection.ListRule)
|
||||||
|
assert.Equal(t, isUser, *systemDetailsCollection.ViewRule)
|
||||||
|
assert.Nil(t, systemDetailsCollection.CreateRule)
|
||||||
|
assert.Nil(t, systemDetailsCollection.UpdateRule)
|
||||||
|
assert.Nil(t, systemDetailsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// system_stats collection
|
||||||
|
systemStatsCollection, err := hub.FindCollectionByNameOrId("system_stats")
|
||||||
|
require.NoError(t, err, "Failed to find system_stats collection")
|
||||||
|
assert.Equal(t, isUser, *systemStatsCollection.ListRule)
|
||||||
|
assert.Nil(t, systemStatsCollection.ViewRule)
|
||||||
|
assert.Nil(t, systemStatsCollection.CreateRule)
|
||||||
|
assert.Nil(t, systemStatsCollection.UpdateRule)
|
||||||
|
assert.Nil(t, systemStatsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// systemd_services collection
|
||||||
|
systemdServicesCollection, err := hub.FindCollectionByNameOrId("systemd_services")
|
||||||
|
require.NoError(t, err, "Failed to find systemd_services collection")
|
||||||
|
assert.Equal(t, isUser, *systemdServicesCollection.ListRule)
|
||||||
|
assert.Nil(t, systemdServicesCollection.ViewRule)
|
||||||
|
assert.Nil(t, systemdServicesCollection.CreateRule)
|
||||||
|
assert.Nil(t, systemdServicesCollection.UpdateRule)
|
||||||
|
assert.Nil(t, systemdServicesCollection.DeleteRule)
|
||||||
|
|
||||||
|
// systems collection
|
||||||
|
systemsCollection, err := hub.FindCollectionByNameOrId("systems")
|
||||||
|
require.NoError(t, err, "Failed to find systems collection")
|
||||||
|
assert.Equal(t, isUser, *systemsCollection.ListRule)
|
||||||
|
assert.Equal(t, isUser, *systemsCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserNotReadonly, *systemsCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserNotReadonly, *systemsCollection.UpdateRule)
|
||||||
|
assert.Equal(t, isUserNotReadonly, *systemsCollection.DeleteRule)
|
||||||
|
|
||||||
|
// universal_tokens collection
|
||||||
|
universalTokensCollection, err := hub.FindCollectionByNameOrId("universal_tokens")
|
||||||
|
require.NoError(t, err, "Failed to find universal_tokens collection")
|
||||||
|
assert.Nil(t, universalTokensCollection.ListRule)
|
||||||
|
assert.Nil(t, universalTokensCollection.ViewRule)
|
||||||
|
assert.Nil(t, universalTokensCollection.CreateRule)
|
||||||
|
assert.Nil(t, universalTokensCollection.UpdateRule)
|
||||||
|
assert.Nil(t, universalTokensCollection.DeleteRule)
|
||||||
|
|
||||||
|
// user_settings collection
|
||||||
|
userSettingsCollection, err := hub.FindCollectionByNameOrId("user_settings")
|
||||||
|
require.NoError(t, err, "Failed to find user_settings collection")
|
||||||
|
assert.Equal(t, isUserMatchesUser, *userSettingsCollection.ListRule)
|
||||||
|
assert.Nil(t, userSettingsCollection.ViewRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *userSettingsCollection.CreateRule)
|
||||||
|
assert.Equal(t, isUserMatchesUser, *userSettingsCollection.UpdateRule)
|
||||||
|
assert.Nil(t, userSettingsCollection.DeleteRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDisablePasswordAuth(t *testing.T) {
|
||||||
|
t.Setenv("DISABLE_PASSWORD_AUTH", "true")
|
||||||
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
|
defer hub.Cleanup()
|
||||||
|
|
||||||
|
usersCollection, err := hub.FindCollectionByNameOrId("users")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.False(t, usersCollection.PasswordAuth.Enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserCreation(t *testing.T) {
|
||||||
|
t.Setenv("USER_CREATION", "true")
|
||||||
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
|
defer hub.Cleanup()
|
||||||
|
|
||||||
|
usersCollection, err := hub.FindCollectionByNameOrId("users")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "@request.context = 'oauth2'", *usersCollection.CreateRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMFAOtp(t *testing.T) {
|
||||||
|
t.Setenv("MFA_OTP", "true")
|
||||||
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
|
defer hub.Cleanup()
|
||||||
|
|
||||||
|
usersCollection, err := hub.FindCollectionByNameOrId("users")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, usersCollection.OTP.Enabled)
|
||||||
|
assert.True(t, usersCollection.MFA.Enabled)
|
||||||
|
|
||||||
|
superusersCollection, err := hub.FindCollectionByNameOrId(core.CollectionNameSuperusers)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, superusersCollection.OTP.Enabled)
|
||||||
|
assert.True(t, superusersCollection.MFA.Enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiCollectionsAuthRules(t *testing.T) {
|
||||||
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
|
defer hub.Cleanup()
|
||||||
|
|
||||||
|
hub.StartHub()
|
||||||
|
|
||||||
|
user1, _ := beszelTests.CreateUser(hub, "user1@example.com", "password")
|
||||||
|
user1Token, _ := user1.NewAuthToken()
|
||||||
|
|
||||||
|
user2, _ := beszelTests.CreateUser(hub, "user2@example.com", "password")
|
||||||
|
// user2Token, _ := user2.NewAuthToken()
|
||||||
|
|
||||||
|
userReadonly, _ := beszelTests.CreateUserWithRole(hub, "userreadonly@example.com", "password", "readonly")
|
||||||
|
userReadonlyToken, _ := userReadonly.NewAuthToken()
|
||||||
|
|
||||||
|
userOneSystem, _ := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||||
|
"name": "system1",
|
||||||
|
"users": []string{user1.Id},
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
})
|
||||||
|
|
||||||
|
sharedSystem, _ := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||||
|
"name": "system2",
|
||||||
|
"users": []string{user1.Id, user2.Id},
|
||||||
|
"host": "127.0.0.2",
|
||||||
|
})
|
||||||
|
|
||||||
|
userTwoSystem, _ := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||||
|
"name": "system3",
|
||||||
|
"users": []string{user2.Id},
|
||||||
|
"host": "127.0.0.2",
|
||||||
|
})
|
||||||
|
|
||||||
|
userRecords, _ := hub.CountRecords("users")
|
||||||
|
assert.EqualValues(t, 3, userRecords, "all users should be created")
|
||||||
|
|
||||||
|
systemRecords, _ := hub.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 3, systemRecords, "all systems should be created")
|
||||||
|
|
||||||
|
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
||||||
|
return hub.TestApp
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []beszelTests.ApiScenario{
|
||||||
|
{
|
||||||
|
Name: "Unauthorized user cannot list systems",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
URL: "/api/collections/systems/records",
|
||||||
|
ExpectedStatus: 200, // https://github.com/pocketbase/pocketbase/discussions/1570
|
||||||
|
TestAppFactory: testAppFactory,
|
||||||
|
ExpectedContent: []string{`"items":[]`, `"totalItems":0`},
|
||||||
|
NotExpectedContent: []string{userOneSystem.Id, sharedSystem.Id, userTwoSystem.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Unauthorized user cannot delete a system",
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
URL: fmt.Sprintf("/api/collections/systems/records/%s", userOneSystem.Id),
|
||||||
|
ExpectedStatus: 404,
|
||||||
|
TestAppFactory: testAppFactory,
|
||||||
|
ExpectedContent: []string{"resource wasn't found"},
|
||||||
|
NotExpectedContent: []string{userOneSystem.Id},
|
||||||
|
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 3, systemsCount, "should have 3 systems before deletion")
|
||||||
|
},
|
||||||
|
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 3, systemsCount, "should still have 3 systems after failed deletion")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "User 1 can list their own systems",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
URL: "/api/collections/systems/records",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Authorization": user1Token,
|
||||||
|
},
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
ExpectedContent: []string{userOneSystem.Id, sharedSystem.Id},
|
||||||
|
NotExpectedContent: []string{userTwoSystem.Id},
|
||||||
|
TestAppFactory: testAppFactory,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "User 1 cannot list user 2's system",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
URL: "/api/collections/systems/records",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Authorization": user1Token,
|
||||||
|
},
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
ExpectedContent: []string{userOneSystem.Id, sharedSystem.Id},
|
||||||
|
NotExpectedContent: []string{userTwoSystem.Id},
|
||||||
|
TestAppFactory: testAppFactory,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "User 1 can see user 2's system if SHARE_ALL_SYSTEMS is enabled",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
URL: "/api/collections/systems/records",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Authorization": user1Token,
|
||||||
|
},
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
ExpectedContent: []string{userOneSystem.Id, sharedSystem.Id, userTwoSystem.Id},
|
||||||
|
TestAppFactory: testAppFactory,
|
||||||
|
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||||
|
t.Setenv("SHARE_ALL_SYSTEMS", "true")
|
||||||
|
hub.SetCollectionAuthSettings()
|
||||||
|
},
|
||||||
|
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||||
|
t.Setenv("SHARE_ALL_SYSTEMS", "")
|
||||||
|
hub.SetCollectionAuthSettings()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "User 1 can delete their own system",
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
URL: fmt.Sprintf("/api/collections/systems/records/%s", userOneSystem.Id),
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Authorization": user1Token,
|
||||||
|
},
|
||||||
|
ExpectedStatus: 204,
|
||||||
|
TestAppFactory: testAppFactory,
|
||||||
|
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 3, systemsCount, "should have 3 systems before deletion")
|
||||||
|
},
|
||||||
|
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 2, systemsCount, "should have 2 systems after deletion")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "User 1 cannot delete user 2's system",
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
URL: fmt.Sprintf("/api/collections/systems/records/%s", userTwoSystem.Id),
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Authorization": user1Token,
|
||||||
|
},
|
||||||
|
ExpectedStatus: 404,
|
||||||
|
TestAppFactory: testAppFactory,
|
||||||
|
ExpectedContent: []string{"resource wasn't found"},
|
||||||
|
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 2, systemsCount)
|
||||||
|
},
|
||||||
|
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 2, systemsCount)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Readonly cannot delete a system even if SHARE_ALL_SYSTEMS is enabled",
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
URL: fmt.Sprintf("/api/collections/systems/records/%s", sharedSystem.Id),
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Authorization": userReadonlyToken,
|
||||||
|
},
|
||||||
|
ExpectedStatus: 404,
|
||||||
|
ExpectedContent: []string{"resource wasn't found"},
|
||||||
|
TestAppFactory: testAppFactory,
|
||||||
|
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||||
|
t.Setenv("SHARE_ALL_SYSTEMS", "true")
|
||||||
|
hub.SetCollectionAuthSettings()
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 2, systemsCount)
|
||||||
|
},
|
||||||
|
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||||
|
t.Setenv("SHARE_ALL_SYSTEMS", "")
|
||||||
|
hub.SetCollectionAuthSettings()
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 2, systemsCount)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "User 1 can delete user 2's system if SHARE_ALL_SYSTEMS is enabled",
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
URL: fmt.Sprintf("/api/collections/systems/records/%s", userTwoSystem.Id),
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Authorization": user1Token,
|
||||||
|
},
|
||||||
|
ExpectedStatus: 204,
|
||||||
|
TestAppFactory: testAppFactory,
|
||||||
|
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||||
|
t.Setenv("SHARE_ALL_SYSTEMS", "true")
|
||||||
|
hub.SetCollectionAuthSettings()
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 2, systemsCount)
|
||||||
|
},
|
||||||
|
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||||
|
t.Setenv("SHARE_ALL_SYSTEMS", "")
|
||||||
|
hub.SetCollectionAuthSettings()
|
||||||
|
systemsCount, _ := app.CountRecords("systems")
|
||||||
|
assert.EqualValues(t, 1, systemsCount)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scenario := range scenarios {
|
||||||
|
scenario.Test(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ package hub
|
|||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -45,20 +46,17 @@ type Hub struct {
|
|||||||
var containerIDPattern = regexp.MustCompile(`^[a-fA-F0-9]{12,64}$`)
|
var containerIDPattern = regexp.MustCompile(`^[a-fA-F0-9]{12,64}$`)
|
||||||
|
|
||||||
// NewHub creates a new Hub instance with default configuration
|
// NewHub creates a new Hub instance with default configuration
|
||||||
func NewHub(app core.App) *Hub {
|
func NewHub(app core.App) (*Hub, error) {
|
||||||
hub := &Hub{}
|
hub := &Hub{App: app}
|
||||||
hub.App = app
|
|
||||||
|
|
||||||
hub.AlertManager = alerts.NewAlertManager(hub)
|
hub.AlertManager = alerts.NewAlertManager(hub)
|
||||||
hub.um = users.NewUserManager(hub)
|
hub.um = users.NewUserManager(hub)
|
||||||
hub.rm = records.NewRecordManager(hub)
|
hub.rm = records.NewRecordManager(hub)
|
||||||
hub.sm = systems.NewSystemManager(hub)
|
hub.sm = systems.NewSystemManager(hub)
|
||||||
hub.appURL, _ = GetEnv("APP_URL")
|
|
||||||
hub.hb = heartbeat.New(app, GetEnv)
|
hub.hb = heartbeat.New(app, GetEnv)
|
||||||
if hub.hb != nil {
|
if hub.hb != nil {
|
||||||
hub.hbStop = make(chan struct{})
|
hub.hbStop = make(chan struct{})
|
||||||
}
|
}
|
||||||
return hub
|
return hub, initialize(hub)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnv retrieves an environment variable with a "BESZEL_HUB_" prefix, or falls back to the unprefixed key.
|
// GetEnv retrieves an environment variable with a "BESZEL_HUB_" prefix, or falls back to the unprefixed key.
|
||||||
@@ -72,10 +70,6 @@ func GetEnv(key string) (value string, exists bool) {
|
|||||||
|
|
||||||
func (h *Hub) StartHub() error {
|
func (h *Hub) StartHub() error {
|
||||||
h.App.OnServe().BindFunc(func(e *core.ServeEvent) error {
|
h.App.OnServe().BindFunc(func(e *core.ServeEvent) error {
|
||||||
// initialize settings / collections
|
|
||||||
if err := h.initialize(e); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// sync systems with config
|
// sync systems with config
|
||||||
if err := config.SyncSystems(e); err != nil {
|
if err := config.SyncSystems(e); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -110,132 +104,32 @@ func (h *Hub) StartHub() error {
|
|||||||
h.App.OnRecordCreate("users").BindFunc(h.um.InitializeUserRole)
|
h.App.OnRecordCreate("users").BindFunc(h.um.InitializeUserRole)
|
||||||
h.App.OnRecordCreate("user_settings").BindFunc(h.um.InitializeUserSettings)
|
h.App.OnRecordCreate("user_settings").BindFunc(h.um.InitializeUserSettings)
|
||||||
|
|
||||||
if pb, ok := h.App.(*pocketbase.PocketBase); ok {
|
pb, ok := h.App.(*pocketbase.PocketBase)
|
||||||
// log.Println("Starting pocketbase")
|
if !ok {
|
||||||
err := pb.Start()
|
return errors.New("not a pocketbase app")
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
return pb.Start()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize sets up initial configuration (collections, settings, etc.)
|
// initialize sets up initial configuration (collections, settings, etc.)
|
||||||
func (h *Hub) initialize(e *core.ServeEvent) error {
|
func initialize(hub *Hub) error {
|
||||||
// set general settings
|
if !hub.App.IsBootstrapped() {
|
||||||
settings := e.App.Settings()
|
hub.App.Bootstrap()
|
||||||
// batch requests (for global alerts)
|
|
||||||
settings.Batch.Enabled = true
|
|
||||||
// set URL if BASE_URL env is set
|
|
||||||
if h.appURL != "" {
|
|
||||||
settings.Meta.AppURL = h.appURL
|
|
||||||
}
|
}
|
||||||
if err := e.App.Save(settings); err != nil {
|
// set general settings
|
||||||
|
settings := hub.App.Settings()
|
||||||
|
// batch requests (for alerts)
|
||||||
|
settings.Batch.Enabled = true
|
||||||
|
// set URL if APP_URL env is set
|
||||||
|
if appURL, isSet := GetEnv("APP_URL"); isSet {
|
||||||
|
hub.appURL = appURL
|
||||||
|
settings.Meta.AppURL = hub.appURL
|
||||||
|
}
|
||||||
|
if err := hub.App.Save(settings); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// set auth settings
|
// set auth settings
|
||||||
if err := setCollectionAuthSettings(e.App); err != nil {
|
return setCollectionAuthSettings(hub.App)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// setCollectionAuthSettings sets up default authentication settings for the app
|
|
||||||
func setCollectionAuthSettings(app core.App) error {
|
|
||||||
usersCollection, err := app.FindCollectionByNameOrId("users")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
superusersCollection, err := app.FindCollectionByNameOrId(core.CollectionNameSuperusers)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable email auth if DISABLE_PASSWORD_AUTH env var is set
|
|
||||||
disablePasswordAuth, _ := GetEnv("DISABLE_PASSWORD_AUTH")
|
|
||||||
usersCollection.PasswordAuth.Enabled = disablePasswordAuth != "true"
|
|
||||||
usersCollection.PasswordAuth.IdentityFields = []string{"email"}
|
|
||||||
// allow oauth user creation if USER_CREATION is set
|
|
||||||
if userCreation, _ := GetEnv("USER_CREATION"); userCreation == "true" {
|
|
||||||
cr := "@request.context = 'oauth2'"
|
|
||||||
usersCollection.CreateRule = &cr
|
|
||||||
} else {
|
|
||||||
usersCollection.CreateRule = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable mfaOtp mfa if MFA_OTP env var is set
|
|
||||||
mfaOtp, _ := GetEnv("MFA_OTP")
|
|
||||||
usersCollection.OTP.Length = 6
|
|
||||||
superusersCollection.OTP.Length = 6
|
|
||||||
usersCollection.OTP.Enabled = mfaOtp == "true"
|
|
||||||
usersCollection.MFA.Enabled = mfaOtp == "true"
|
|
||||||
superusersCollection.OTP.Enabled = mfaOtp == "true" || mfaOtp == "superusers"
|
|
||||||
superusersCollection.MFA.Enabled = mfaOtp == "true" || mfaOtp == "superusers"
|
|
||||||
if err := app.Save(superusersCollection); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := app.Save(usersCollection); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
shareAllSystems, _ := GetEnv("SHARE_ALL_SYSTEMS")
|
|
||||||
|
|
||||||
// allow all users to access systems if SHARE_ALL_SYSTEMS is set
|
|
||||||
systemsCollection, err := app.FindCollectionByNameOrId("systems")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var systemsReadRule string
|
|
||||||
if shareAllSystems == "true" {
|
|
||||||
systemsReadRule = "@request.auth.id != \"\""
|
|
||||||
} else {
|
|
||||||
systemsReadRule = "@request.auth.id != \"\" && users.id ?= @request.auth.id"
|
|
||||||
}
|
|
||||||
updateDeleteRule := systemsReadRule + " && @request.auth.role != \"readonly\""
|
|
||||||
systemsCollection.ListRule = &systemsReadRule
|
|
||||||
systemsCollection.ViewRule = &systemsReadRule
|
|
||||||
systemsCollection.UpdateRule = &updateDeleteRule
|
|
||||||
systemsCollection.DeleteRule = &updateDeleteRule
|
|
||||||
if err := app.Save(systemsCollection); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow all users to access all containers if SHARE_ALL_SYSTEMS is set
|
|
||||||
containersCollection, err := app.FindCollectionByNameOrId("containers")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
containersListRule := strings.Replace(systemsReadRule, "users.id", "system.users.id", 1)
|
|
||||||
containersCollection.ListRule = &containersListRule
|
|
||||||
if err := app.Save(containersCollection); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow all users to access system-related collections if SHARE_ALL_SYSTEMS is set
|
|
||||||
// these collections all have a "system" relation field
|
|
||||||
systemRelatedCollections := []string{"system_details", "smart_devices", "systemd_services"}
|
|
||||||
for _, collectionName := range systemRelatedCollections {
|
|
||||||
collection, err := app.FindCollectionByNameOrId(collectionName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
collection.ListRule = &containersListRule
|
|
||||||
// set viewRule for collections that need it (system_details, smart_devices)
|
|
||||||
if collection.ViewRule != nil {
|
|
||||||
collection.ViewRule = &containersListRule
|
|
||||||
}
|
|
||||||
// set deleteRule for smart_devices (allows user to dismiss disk warnings)
|
|
||||||
if collectionName == "smart_devices" {
|
|
||||||
deleteRule := containersListRule + " && @request.auth.role != \"readonly\""
|
|
||||||
collection.DeleteRule = &deleteRule
|
|
||||||
}
|
|
||||||
if err := app.Save(collection); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerCronJobs sets up scheduled tasks
|
// registerCronJobs sets up scheduled tasks
|
||||||
|
|||||||
@@ -733,10 +733,8 @@ func TestFirstUserCreation(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("CreateUserEndpoint not available when USER_EMAIL, USER_PASSWORD are set", func(t *testing.T) {
|
t.Run("CreateUserEndpoint not available when USER_EMAIL, USER_PASSWORD are set", func(t *testing.T) {
|
||||||
os.Setenv("BESZEL_HUB_USER_EMAIL", "me@example.com")
|
t.Setenv("BESZEL_HUB_USER_EMAIL", "me@example.com")
|
||||||
os.Setenv("BESZEL_HUB_USER_PASSWORD", "password123")
|
t.Setenv("BESZEL_HUB_USER_PASSWORD", "password123")
|
||||||
defer os.Unsetenv("BESZEL_HUB_USER_EMAIL")
|
|
||||||
defer os.Unsetenv("BESZEL_HUB_USER_PASSWORD")
|
|
||||||
|
|
||||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
defer hub.Cleanup()
|
defer hub.Cleanup()
|
||||||
@@ -852,13 +850,12 @@ func TestAutoLoginMiddleware(t *testing.T) {
|
|||||||
var hubs []*beszelTests.TestHub
|
var hubs []*beszelTests.TestHub
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
defer os.Unsetenv("AUTO_LOGIN")
|
|
||||||
for _, hub := range hubs {
|
for _, hub := range hubs {
|
||||||
hub.Cleanup()
|
hub.Cleanup()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
os.Setenv("AUTO_LOGIN", "user@test.com")
|
t.Setenv("AUTO_LOGIN", "user@test.com")
|
||||||
|
|
||||||
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
||||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
@@ -906,13 +903,12 @@ func TestTrustedHeaderMiddleware(t *testing.T) {
|
|||||||
var hubs []*beszelTests.TestHub
|
var hubs []*beszelTests.TestHub
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
defer os.Unsetenv("TRUSTED_AUTH_HEADER")
|
|
||||||
for _, hub := range hubs {
|
for _, hub := range hubs {
|
||||||
hub.Cleanup()
|
hub.Cleanup()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
os.Setenv("TRUSTED_AUTH_HEADER", "X-Beszel-Trusted")
|
t.Setenv("TRUSTED_AUTH_HEADER", "X-Beszel-Trusted")
|
||||||
|
|
||||||
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
||||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
@@ -961,3 +957,21 @@ func TestTrustedHeaderMiddleware(t *testing.T) {
|
|||||||
scenario.Test(t)
|
scenario.Test(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAppUrl(t *testing.T) {
|
||||||
|
t.Run("no APP_URL does't change app url", func(t *testing.T) {
|
||||||
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
|
defer hub.Cleanup()
|
||||||
|
|
||||||
|
settings := hub.Settings()
|
||||||
|
assert.Equal(t, "http://localhost:8090", settings.Meta.AppURL)
|
||||||
|
})
|
||||||
|
t.Run("APP_URL changes app url", func(t *testing.T) {
|
||||||
|
t.Setenv("APP_URL", "http://example.com/app")
|
||||||
|
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||||
|
defer hub.Cleanup()
|
||||||
|
|
||||||
|
settings := hub.Settings()
|
||||||
|
assert.Equal(t, "http://example.com/app", settings.Meta.AppURL)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
package hub
|
package hub
|
||||||
|
|
||||||
import "github.com/henrygd/beszel/internal/hub/systems"
|
import (
|
||||||
|
"github.com/henrygd/beszel/internal/hub/systems"
|
||||||
|
)
|
||||||
|
|
||||||
// TESTING ONLY: GetSystemManager returns the system manager
|
// TESTING ONLY: GetSystemManager returns the system manager
|
||||||
func (h *Hub) GetSystemManager() *systems.SystemManager {
|
func (h *Hub) GetSystemManager() *systems.SystemManager {
|
||||||
@@ -18,3 +20,7 @@ func (h *Hub) GetPubkey() string {
|
|||||||
func (h *Hub) SetPubkey(pubkey string) {
|
func (h *Hub) SetPubkey(pubkey string) {
|
||||||
h.pubKey = pubkey
|
h.pubKey = pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Hub) SetCollectionAuthSettings() error {
|
||||||
|
return setCollectionAuthSettings(h)
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ func init() {
|
|||||||
jsonData := `[
|
jsonData := `[
|
||||||
{
|
{
|
||||||
"id": "elngm8x1l60zi2v",
|
"id": "elngm8x1l60zi2v",
|
||||||
"listRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"listRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"viewRule": "",
|
"viewRule": null,
|
||||||
"createRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"createRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"updateRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"updateRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"deleteRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"deleteRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"name": "alerts",
|
"name": "alerts",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"fields": [
|
"fields": [
|
||||||
@@ -143,11 +143,11 @@ func init() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "pbc_1697146157",
|
"id": "pbc_1697146157",
|
||||||
"listRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"listRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"viewRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"viewRule": null,
|
||||||
"createRule": null,
|
"createRule": null,
|
||||||
"updateRule": null,
|
"updateRule": null,
|
||||||
"deleteRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"deleteRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"name": "alerts_history",
|
"name": "alerts_history",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"fields": [
|
"fields": [
|
||||||
@@ -261,7 +261,7 @@ func init() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "juohu4jipgc13v7",
|
"id": "juohu4jipgc13v7",
|
||||||
"listRule": "@request.auth.id != \"\"",
|
"listRule": null,
|
||||||
"viewRule": null,
|
"viewRule": null,
|
||||||
"createRule": null,
|
"createRule": null,
|
||||||
"updateRule": null,
|
"updateRule": null,
|
||||||
@@ -351,10 +351,10 @@ func init() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "pbc_3663931638",
|
"id": "pbc_3663931638",
|
||||||
"listRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
"listRule": null,
|
||||||
"viewRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
"viewRule": null,
|
||||||
"createRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id && @request.auth.role != \"readonly\"",
|
"createRule": null,
|
||||||
"updateRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id && @request.auth.role != \"readonly\"",
|
"updateRule": null,
|
||||||
"deleteRule": null,
|
"deleteRule": null,
|
||||||
"name": "fingerprints",
|
"name": "fingerprints",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
@@ -433,7 +433,7 @@ func init() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ej9oowivz8b2mht",
|
"id": "ej9oowivz8b2mht",
|
||||||
"listRule": "@request.auth.id != \"\"",
|
"listRule": null,
|
||||||
"viewRule": null,
|
"viewRule": null,
|
||||||
"createRule": null,
|
"createRule": null,
|
||||||
"updateRule": null,
|
"updateRule": null,
|
||||||
@@ -523,10 +523,10 @@ func init() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "4afacsdnlu8q8r2",
|
"id": "4afacsdnlu8q8r2",
|
||||||
"listRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"listRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"viewRule": null,
|
"viewRule": null,
|
||||||
"createRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"createRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"updateRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"updateRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"deleteRule": null,
|
"deleteRule": null,
|
||||||
"name": "user_settings",
|
"name": "user_settings",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
@@ -596,11 +596,11 @@ func init() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "2hz5ncl8tizk5nx",
|
"id": "2hz5ncl8tizk5nx",
|
||||||
"listRule": "@request.auth.id != \"\" && users.id ?= @request.auth.id",
|
"listRule": null,
|
||||||
"viewRule": "@request.auth.id != \"\" && users.id ?= @request.auth.id",
|
"viewRule": null,
|
||||||
"createRule": "@request.auth.id != \"\" && users.id ?= @request.auth.id && @request.auth.role != \"readonly\"",
|
"createRule": null,
|
||||||
"updateRule": "@request.auth.id != \"\" && users.id ?= @request.auth.id && @request.auth.role != \"readonly\"",
|
"updateRule": null,
|
||||||
"deleteRule": "@request.auth.id != \"\" && users.id ?= @request.auth.id && @request.auth.role != \"readonly\"",
|
"deleteRule": null,
|
||||||
"name": "systems",
|
"name": "systems",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"fields": [
|
"fields": [
|
||||||
@@ -866,7 +866,7 @@ func init() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "pbc_1864144027",
|
"id": "pbc_1864144027",
|
||||||
"listRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
"listRule": null,
|
||||||
"viewRule": null,
|
"viewRule": null,
|
||||||
"createRule": null,
|
"createRule": null,
|
||||||
"updateRule": null,
|
"updateRule": null,
|
||||||
@@ -1159,7 +1159,7 @@ func init() {
|
|||||||
"CREATE INDEX ` + "`" + `idx_4Z7LuLNdQb` + "`" + ` ON ` + "`" + `systemd_services` + "`" + ` (` + "`" + `system` + "`" + `)",
|
"CREATE INDEX ` + "`" + `idx_4Z7LuLNdQb` + "`" + ` ON ` + "`" + `systemd_services` + "`" + ` (` + "`" + `system` + "`" + `)",
|
||||||
"CREATE INDEX ` + "`" + `idx_pBp1fF837e` + "`" + ` ON ` + "`" + `systemd_services` + "`" + ` (` + "`" + `updated` + "`" + `)"
|
"CREATE INDEX ` + "`" + `idx_pBp1fF837e` + "`" + ` ON ` + "`" + `systemd_services` + "`" + ` (` + "`" + `updated` + "`" + `)"
|
||||||
],
|
],
|
||||||
"listRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
"listRule": null,
|
||||||
"name": "systemd_services",
|
"name": "systemd_services",
|
||||||
"system": false,
|
"system": false,
|
||||||
"type": "base",
|
"type": "base",
|
||||||
@@ -1167,8 +1167,8 @@ func init() {
|
|||||||
"viewRule": null
|
"viewRule": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"createRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"createRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"deleteRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"deleteRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"autogeneratePattern": "[a-z0-9]{10}",
|
"autogeneratePattern": "[a-z0-9]{10}",
|
||||||
@@ -1252,16 +1252,16 @@ func init() {
|
|||||||
"CREATE INDEX ` + "`" + `idx_q0iKnRP9v8` + "`" + ` ON ` + "`" + `quiet_hours` + "`" + ` (\n ` + "`" + `user` + "`" + `,\n ` + "`" + `system` + "`" + `\n)",
|
"CREATE INDEX ` + "`" + `idx_q0iKnRP9v8` + "`" + ` ON ` + "`" + `quiet_hours` + "`" + ` (\n ` + "`" + `user` + "`" + `,\n ` + "`" + `system` + "`" + `\n)",
|
||||||
"CREATE INDEX ` + "`" + `idx_6T7ljT7FJd` + "`" + ` ON ` + "`" + `quiet_hours` + "`" + ` (\n ` + "`" + `type` + "`" + `,\n ` + "`" + `end` + "`" + `\n)"
|
"CREATE INDEX ` + "`" + `idx_6T7ljT7FJd` + "`" + ` ON ` + "`" + `quiet_hours` + "`" + ` (\n ` + "`" + `type` + "`" + `,\n ` + "`" + `end` + "`" + `\n)"
|
||||||
],
|
],
|
||||||
"listRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"listRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"name": "quiet_hours",
|
"name": "quiet_hours",
|
||||||
"system": false,
|
"system": false,
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"updateRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
"updateRule": "@request.auth.id != \"\" && user = @request.auth.id",
|
||||||
"viewRule": "@request.auth.id != \"\" && user.id = @request.auth.id"
|
"viewRule": "@request.auth.id != \"\" && user = @request.auth.id"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"createRule": null,
|
"createRule": null,
|
||||||
"deleteRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
"deleteRule": null,
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"autogeneratePattern": "[a-z0-9]{10}",
|
"autogeneratePattern": "[a-z0-9]{10}",
|
||||||
@@ -1447,16 +1447,16 @@ func init() {
|
|||||||
"indexes": [
|
"indexes": [
|
||||||
"CREATE INDEX ` + "`" + `idx_DZ9yhvgl44` + "`" + ` ON ` + "`" + `smart_devices` + "`" + ` (` + "`" + `system` + "`" + `)"
|
"CREATE INDEX ` + "`" + `idx_DZ9yhvgl44` + "`" + ` ON ` + "`" + `smart_devices` + "`" + ` (` + "`" + `system` + "`" + `)"
|
||||||
],
|
],
|
||||||
"listRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
"listRule": null,
|
||||||
"name": "smart_devices",
|
"name": "smart_devices",
|
||||||
"system": false,
|
"system": false,
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"updateRule": null,
|
"updateRule": null,
|
||||||
"viewRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id"
|
"viewRule": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"createRule": "",
|
"createRule": null,
|
||||||
"deleteRule": "",
|
"deleteRule": null,
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"autogeneratePattern": "[a-z0-9]{15}",
|
"autogeneratePattern": "[a-z0-9]{15}",
|
||||||
@@ -1625,12 +1625,12 @@ func init() {
|
|||||||
],
|
],
|
||||||
"id": "pbc_3116237454",
|
"id": "pbc_3116237454",
|
||||||
"indexes": [],
|
"indexes": [],
|
||||||
"listRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
|
||||||
"name": "system_details",
|
"name": "system_details",
|
||||||
"system": false,
|
"system": false,
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"updateRule": "",
|
"updateRule": null,
|
||||||
"viewRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id"
|
"listRule": null,
|
||||||
|
"viewRule": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"createRule": null,
|
"createRule": null,
|
||||||
|
|||||||
@@ -52,7 +52,10 @@ func NewTestHubWithConfig(config core.BaseAppConfig) (*TestHub, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hub := hub.NewHub(testApp)
|
hub, err := hub.NewHub(testApp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
t := &TestHub{
|
t := &TestHub{
|
||||||
App: testApp,
|
App: testApp,
|
||||||
@@ -77,6 +80,16 @@ func CreateUser(app core.App, email string, password string) (*core.Record, erro
|
|||||||
return user, app.Save(user)
|
return user, app.Save(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateUserWithRole(app core.App, email string, password string, roleName string) (*core.Record, error) {
|
||||||
|
user, err := CreateUser(app, email, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Set("role", roleName)
|
||||||
|
return user, app.Save(user)
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to create a test record
|
// Helper function to create a test record
|
||||||
func CreateRecord(app core.App, collectionName string, fields map[string]any) (*core.Record, error) {
|
func CreateRecord(app core.App, collectionName string, fields map[string]any) (*core.Record, error) {
|
||||||
collection, err := app.FindCachedCollectionByNameOrId(collectionName)
|
collection, err := app.FindCachedCollectionByNameOrId(collectionName)
|
||||||
|
|||||||
Reference in New Issue
Block a user