mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-30 22:26:42 +00:00
Test up tests users if none are available on CI
This commit is contained in:
@@ -7,12 +7,14 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
cryptossh "golang.org/x/crypto/ssh"
|
||||
@@ -21,6 +23,17 @@ import (
|
||||
sshserver "github.com/netbirdio/netbird/client/ssh/server"
|
||||
)
|
||||
|
||||
// TestMain handles package-level setup and cleanup
|
||||
func TestMain(m *testing.M) {
|
||||
// Run tests
|
||||
code := m.Run()
|
||||
|
||||
// Cleanup any created test users
|
||||
cleanupTestUsers()
|
||||
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func TestSSHClient_DialWithKey(t *testing.T) {
|
||||
// Generate host key for server
|
||||
hostKey, err := ssh.GeneratePrivateKey(ssh.ED25519)
|
||||
@@ -417,7 +430,11 @@ func TestSSHClient_PortForwardingDataTransfer(t *testing.T) {
|
||||
go func() {
|
||||
err := client.LocalPortForward(ctx, localAddr, testServerAddr)
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
t.Logf("Port forward error: %v", err)
|
||||
if isWindowsPrivilegeError(err) {
|
||||
t.Logf("Port forward failed due to Windows privilege restrictions: %v", err)
|
||||
} else {
|
||||
t.Logf("Port forward error: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -448,6 +465,23 @@ func TestSSHClient_PortForwardingDataTransfer(t *testing.T) {
|
||||
func getCurrentUsername() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
if currentUser, err := user.Current(); err == nil {
|
||||
// Check if this is a system account that can't authenticate
|
||||
if isSystemAccount(currentUser.Username) {
|
||||
// In CI environments, create a test user; otherwise try Administrator
|
||||
if isCI() {
|
||||
if testUser := getOrCreateTestUser(); testUser != "" {
|
||||
return testUser
|
||||
}
|
||||
} else {
|
||||
// Try Administrator first for local development
|
||||
if _, err := user.Lookup("Administrator"); err == nil {
|
||||
return "Administrator"
|
||||
}
|
||||
if testUser := getOrCreateTestUser(); testUser != "" {
|
||||
return testUser
|
||||
}
|
||||
}
|
||||
}
|
||||
// On Windows, return the full domain\username for proper authentication
|
||||
return currentUser.Username
|
||||
}
|
||||
@@ -463,3 +497,154 @@ func getCurrentUsername() string {
|
||||
|
||||
return "test-user"
|
||||
}
|
||||
|
||||
// isCI checks if we're running in a CI environment
|
||||
func isCI() bool {
|
||||
ciEnvVars := []string{
|
||||
"CI", "CONTINUOUS_INTEGRATION", "GITHUB_ACTIONS",
|
||||
"GITLAB_CI", "JENKINS_URL", "BUILDKITE", "CIRCLECI",
|
||||
}
|
||||
|
||||
for _, envVar := range ciEnvVars {
|
||||
if os.Getenv(envVar) != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getOrCreateTestUser creates a test user on Windows if needed
|
||||
func getOrCreateTestUser() string {
|
||||
testUsername := "netbird-test-user"
|
||||
|
||||
// Check if user already exists
|
||||
if _, err := user.Lookup(testUsername); err == nil {
|
||||
return testUsername
|
||||
}
|
||||
|
||||
// Try to create the user using PowerShell
|
||||
if createWindowsTestUser(testUsername) {
|
||||
// Register cleanup for the test user
|
||||
registerTestUserCleanup(testUsername)
|
||||
return testUsername
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
var createdTestUsers = make(map[string]bool)
|
||||
var testUsersToCleanup []string
|
||||
|
||||
// registerTestUserCleanup registers a test user for cleanup
|
||||
func registerTestUserCleanup(username string) {
|
||||
if !createdTestUsers[username] {
|
||||
createdTestUsers[username] = true
|
||||
testUsersToCleanup = append(testUsersToCleanup, username)
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupTestUsers removes all created test users
|
||||
func cleanupTestUsers() {
|
||||
for _, username := range testUsersToCleanup {
|
||||
removeWindowsTestUser(username)
|
||||
}
|
||||
testUsersToCleanup = nil
|
||||
createdTestUsers = make(map[string]bool)
|
||||
}
|
||||
|
||||
// removeWindowsTestUser removes a local user on Windows using PowerShell
|
||||
func removeWindowsTestUser(username string) {
|
||||
if runtime.GOOS != "windows" {
|
||||
return
|
||||
}
|
||||
|
||||
// PowerShell command to remove a local user
|
||||
psCmd := fmt.Sprintf(`
|
||||
try {
|
||||
Remove-LocalUser -Name "%s" -ErrorAction Stop
|
||||
Write-Output "User removed successfully"
|
||||
} catch {
|
||||
if ($_.Exception.Message -like "*cannot be found*") {
|
||||
Write-Output "User not found (already removed)"
|
||||
} else {
|
||||
Write-Error $_.Exception.Message
|
||||
}
|
||||
}
|
||||
`, username)
|
||||
|
||||
cmd := exec.Command("powershell", "-Command", psCmd)
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed to remove test user %s: %v, output: %s", username, err, string(output))
|
||||
} else {
|
||||
log.Printf("Test user %s cleanup result: %s", username, string(output))
|
||||
}
|
||||
}
|
||||
|
||||
// createWindowsTestUser creates a local user on Windows using PowerShell
|
||||
func createWindowsTestUser(username string) bool {
|
||||
if runtime.GOOS != "windows" {
|
||||
return false
|
||||
}
|
||||
|
||||
// PowerShell command to create a local user
|
||||
psCmd := fmt.Sprintf(`
|
||||
try {
|
||||
$password = ConvertTo-SecureString "TestPassword123!" -AsPlainText -Force
|
||||
New-LocalUser -Name "%s" -Password $password -Description "NetBird test user" -UserMayNotChangePassword -PasswordNeverExpires
|
||||
Add-LocalGroupMember -Group "Users" -Member "%s"
|
||||
Write-Output "User created successfully"
|
||||
} catch {
|
||||
if ($_.Exception.Message -like "*already exists*") {
|
||||
Write-Output "User already exists"
|
||||
} else {
|
||||
Write-Error $_.Exception.Message
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
`, username, username)
|
||||
|
||||
cmd := exec.Command("powershell", "-Command", psCmd)
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed to create test user: %v, output: %s", err, string(output))
|
||||
return false
|
||||
}
|
||||
|
||||
log.Printf("Test user creation result: %s", string(output))
|
||||
return true
|
||||
}
|
||||
|
||||
// isSystemAccount checks if the user is a system account that can't authenticate
|
||||
func isSystemAccount(username string) bool {
|
||||
systemAccounts := []string{
|
||||
"system",
|
||||
"NT AUTHORITY\\SYSTEM",
|
||||
"NT AUTHORITY\\LOCAL SERVICE",
|
||||
"NT AUTHORITY\\NETWORK SERVICE",
|
||||
}
|
||||
|
||||
for _, sysAccount := range systemAccounts {
|
||||
if strings.EqualFold(username, sysAccount) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isWindowsPrivilegeError checks if an error is related to Windows privilege restrictions
|
||||
func isWindowsPrivilegeError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
errStr := strings.ToLower(err.Error())
|
||||
return strings.Contains(errStr, "ntstatus=0xc0000062") || // STATUS_PRIVILEGE_NOT_HELD
|
||||
strings.Contains(errStr, "0xc0000041") || // STATUS_PRIVILEGE_NOT_HELD (LsaRegisterLogonProcess)
|
||||
strings.Contains(errStr, "0xc0000062") || // STATUS_PRIVILEGE_NOT_HELD (LsaLogonUser)
|
||||
strings.Contains(errStr, "privilege") ||
|
||||
strings.Contains(errStr, "access denied") ||
|
||||
strings.Contains(errStr, "user authentication failed")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user