393 lines
11 KiB
Go
393 lines
11 KiB
Go
package main
|
|
|
|
import (
|
|
"acc-server-manager/local/migrations"
|
|
"acc-server-manager/local/model"
|
|
"acc-server-manager/local/repository"
|
|
"acc-server-manager/local/service"
|
|
"acc-server-manager/local/utl/logging"
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
|
|
"github.com/google/uuid"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
func main() {
|
|
// Initialize logging
|
|
logging.Init(true) // Enable debug logging
|
|
|
|
// Create a test database
|
|
testDbPath := "test_migrations.db"
|
|
|
|
// Remove existing test database if it exists
|
|
if fileExists(testDbPath) {
|
|
os.Remove(testDbPath)
|
|
}
|
|
|
|
logging.Info("Creating test database: %s", testDbPath)
|
|
|
|
// Open database connection
|
|
db, err := gorm.Open(sqlite.Open(testDbPath), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Info),
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("Failed to connect to test database: %v", err)
|
|
}
|
|
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
log.Fatalf("Failed to get underlying sql.DB: %v", err)
|
|
}
|
|
defer sqlDB.Close()
|
|
|
|
// Create initial schema with integer IDs to simulate old database
|
|
logging.Info("Creating initial schema with integer IDs...")
|
|
createOldSchema(db)
|
|
|
|
// Insert test data with integer IDs
|
|
logging.Info("Inserting test data...")
|
|
insertTestData(db)
|
|
|
|
// Run UUID migration
|
|
logging.Info("Running UUID migration...")
|
|
if err := migrations.RunUUIDMigration(db); err != nil {
|
|
log.Fatalf("UUID migration failed: %v", err)
|
|
}
|
|
|
|
// Verify migration worked
|
|
logging.Info("Verifying migration results...")
|
|
if err := verifyMigration(db); err != nil {
|
|
log.Fatalf("Migration verification failed: %v", err)
|
|
}
|
|
|
|
// Test role system
|
|
logging.Info("Testing role system...")
|
|
if err := testRoleSystem(db); err != nil {
|
|
log.Fatalf("Role system test failed: %v", err)
|
|
}
|
|
|
|
// Test Super Admin deletion prevention
|
|
logging.Info("Testing Super Admin deletion prevention...")
|
|
if err := testSuperAdminDeletionPrevention(db); err != nil {
|
|
log.Fatalf("Super Admin deletion prevention test failed: %v", err)
|
|
}
|
|
|
|
logging.Info("All tests passed successfully!")
|
|
|
|
// Clean up
|
|
os.Remove(testDbPath)
|
|
logging.Info("Test database cleaned up")
|
|
}
|
|
|
|
func createOldSchema(db *gorm.DB) {
|
|
// Create tables with integer primary keys to simulate old schema
|
|
db.Exec(`
|
|
CREATE TABLE IF NOT EXISTS servers (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
ip TEXT NOT NULL,
|
|
port INTEGER NOT NULL,
|
|
config_path TEXT NOT NULL,
|
|
service_name TEXT NOT NULL,
|
|
date_created DATETIME,
|
|
from_steam_cmd BOOLEAN NOT NULL DEFAULT 1
|
|
)
|
|
`)
|
|
|
|
db.Exec(`
|
|
CREATE TABLE IF NOT EXISTS configs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
server_id INTEGER NOT NULL,
|
|
config_file TEXT NOT NULL,
|
|
old_config TEXT,
|
|
new_config TEXT,
|
|
changed_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`)
|
|
|
|
db.Exec(`
|
|
CREATE TABLE IF NOT EXISTS state_histories (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
server_id INTEGER NOT NULL,
|
|
session TEXT,
|
|
track TEXT,
|
|
player_count INTEGER,
|
|
date_created DATETIME,
|
|
session_start DATETIME,
|
|
session_duration_minutes INTEGER,
|
|
session_id INTEGER NOT NULL DEFAULT 0
|
|
)
|
|
`)
|
|
|
|
db.Exec(`
|
|
CREATE TABLE IF NOT EXISTS steam_credentials (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT NOT NULL,
|
|
password TEXT NOT NULL,
|
|
date_created DATETIME,
|
|
last_updated DATETIME
|
|
)
|
|
`)
|
|
|
|
db.Exec(`
|
|
CREATE TABLE IF NOT EXISTS system_configs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
key TEXT,
|
|
value TEXT,
|
|
default_value TEXT,
|
|
description TEXT,
|
|
date_modified TEXT
|
|
)
|
|
`)
|
|
}
|
|
|
|
func insertTestData(db *gorm.DB) {
|
|
// Insert test server
|
|
db.Exec(`
|
|
INSERT INTO servers (name, ip, port, config_path, service_name, date_created, from_steam_cmd)
|
|
VALUES ('Test Server', '127.0.0.1', 9600, '/test/path', 'TestService', datetime('now'), 1)
|
|
`)
|
|
|
|
// Insert test config
|
|
db.Exec(`
|
|
INSERT INTO configs (server_id, config_file, old_config, new_config)
|
|
VALUES (1, 'test.json', '{"old": true}', '{"new": true}')
|
|
`)
|
|
|
|
// Insert test state history
|
|
db.Exec(`
|
|
INSERT INTO state_histories (server_id, session, track, player_count, date_created, session_duration_minutes, session_id)
|
|
VALUES (1, 'Practice', 'monza', 5, datetime('now'), 60, 1)
|
|
`)
|
|
|
|
// Insert test steam credentials
|
|
db.Exec(`
|
|
INSERT INTO steam_credentials (username, password, date_created, last_updated)
|
|
VALUES ('testuser', 'testpass', datetime('now'), datetime('now'))
|
|
`)
|
|
|
|
// Insert test system config
|
|
db.Exec(`
|
|
INSERT INTO system_configs (key, value, default_value, description, date_modified)
|
|
VALUES ('test_key', 'test_value', 'default_value', 'Test config', datetime('now'))
|
|
`)
|
|
}
|
|
|
|
func verifyMigration(db *gorm.DB) error {
|
|
// Check that all tables now have UUID primary keys
|
|
|
|
// Check servers table
|
|
var serverID string
|
|
err := db.Raw("SELECT id FROM servers LIMIT 1").Scan(&serverID).Error
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query servers table: %v", err)
|
|
}
|
|
if _, err := uuid.Parse(serverID); err != nil {
|
|
return fmt.Errorf("servers table ID is not a valid UUID: %s", serverID)
|
|
}
|
|
|
|
// Check configs table
|
|
var configID, configServerID string
|
|
err = db.Raw("SELECT id, server_id FROM configs LIMIT 1").Row().Scan(&configID, &configServerID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query configs table: %v", err)
|
|
}
|
|
if _, err := uuid.Parse(configID); err != nil {
|
|
return fmt.Errorf("configs table ID is not a valid UUID: %s", configID)
|
|
}
|
|
if _, err := uuid.Parse(configServerID); err != nil {
|
|
return fmt.Errorf("configs table server_id is not a valid UUID: %s", configServerID)
|
|
}
|
|
|
|
// Check state_histories table
|
|
var stateID, stateServerID string
|
|
err = db.Raw("SELECT id, server_id FROM state_histories LIMIT 1").Row().Scan(&stateID, &stateServerID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query state_histories table: %v", err)
|
|
}
|
|
if _, err := uuid.Parse(stateID); err != nil {
|
|
return fmt.Errorf("state_histories table ID is not a valid UUID: %s", stateID)
|
|
}
|
|
if _, err := uuid.Parse(stateServerID); err != nil {
|
|
return fmt.Errorf("state_histories table server_id is not a valid UUID: %s", stateServerID)
|
|
}
|
|
|
|
// Check steam_credentials table
|
|
var steamID string
|
|
err = db.Raw("SELECT id FROM steam_credentials LIMIT 1").Scan(&steamID).Error
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query steam_credentials table: %v", err)
|
|
}
|
|
if _, err := uuid.Parse(steamID); err != nil {
|
|
return fmt.Errorf("steam_credentials table ID is not a valid UUID: %s", steamID)
|
|
}
|
|
|
|
// Check system_configs table
|
|
var systemID string
|
|
err = db.Raw("SELECT id FROM system_configs LIMIT 1").Scan(&systemID).Error
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query system_configs table: %v", err)
|
|
}
|
|
if _, err := uuid.Parse(systemID); err != nil {
|
|
return fmt.Errorf("system_configs table ID is not a valid UUID: %s", systemID)
|
|
}
|
|
|
|
logging.Info("✓ All tables successfully migrated to UUID primary keys")
|
|
return nil
|
|
}
|
|
|
|
func testRoleSystem(db *gorm.DB) error {
|
|
// Auto-migrate the models for role system
|
|
db.AutoMigrate(&model.Role{}, &model.Permission{}, &model.User{})
|
|
|
|
// Create repository and service
|
|
repo := repository.NewMembershipRepository(db)
|
|
service := service.NewMembershipService(repo)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Setup initial data (this should create Super Admin, Admin, and Manager roles)
|
|
if err := service.SetupInitialData(ctx); err != nil {
|
|
return fmt.Errorf("failed to setup initial data: %v", err)
|
|
}
|
|
|
|
// Test that all three roles were created
|
|
roles, err := service.GetAllRoles(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get roles: %v", err)
|
|
}
|
|
|
|
expectedRoles := map[string]bool{
|
|
"Super Admin": false,
|
|
"Admin": false,
|
|
"Manager": false,
|
|
}
|
|
|
|
for _, role := range roles {
|
|
if _, exists := expectedRoles[role.Name]; exists {
|
|
expectedRoles[role.Name] = true
|
|
}
|
|
}
|
|
|
|
for roleName, found := range expectedRoles {
|
|
if !found {
|
|
return fmt.Errorf("role '%s' was not created", roleName)
|
|
}
|
|
}
|
|
|
|
// Test permissions for each role
|
|
superAdminRole, err := repo.FindRoleByName(ctx, "Super Admin")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find Super Admin role: %v", err)
|
|
}
|
|
|
|
adminRole, err := repo.FindRoleByName(ctx, "Admin")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find Admin role: %v", err)
|
|
}
|
|
|
|
managerRole, err := repo.FindRoleByName(ctx, "Manager")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find Manager role: %v", err)
|
|
}
|
|
|
|
// Load permissions for roles
|
|
db.Preload("Permissions").Find(superAdminRole)
|
|
db.Preload("Permissions").Find(adminRole)
|
|
db.Preload("Permissions").Find(managerRole)
|
|
|
|
// Super Admin and Admin should have all permissions
|
|
allPermissions := model.AllPermissions()
|
|
if len(superAdminRole.Permissions) != len(allPermissions) {
|
|
return fmt.Errorf("Super Admin should have all %d permissions, but has %d", len(allPermissions), len(superAdminRole.Permissions))
|
|
}
|
|
|
|
if len(adminRole.Permissions) != len(allPermissions) {
|
|
return fmt.Errorf("Admin should have all %d permissions, but has %d", len(allPermissions), len(adminRole.Permissions))
|
|
}
|
|
|
|
// Manager should have limited permissions (no create/delete for membership, role, user, server)
|
|
expectedManagerPermissions := []string{
|
|
model.ServerView,
|
|
model.ServerUpdate,
|
|
model.ServerStart,
|
|
model.ServerStop,
|
|
model.ConfigView,
|
|
model.ConfigUpdate,
|
|
model.UserView,
|
|
model.RoleView,
|
|
model.MembershipView,
|
|
}
|
|
|
|
if len(managerRole.Permissions) != len(expectedManagerPermissions) {
|
|
return fmt.Errorf("Manager should have %d permissions, but has %d", len(expectedManagerPermissions), len(managerRole.Permissions))
|
|
}
|
|
|
|
// Verify Manager doesn't have restricted permissions
|
|
restrictedPermissions := []string{
|
|
model.ServerCreate,
|
|
model.ServerDelete,
|
|
model.UserCreate,
|
|
model.UserDelete,
|
|
model.RoleCreate,
|
|
model.RoleDelete,
|
|
model.MembershipCreate,
|
|
}
|
|
|
|
for _, restrictedPerm := range restrictedPermissions {
|
|
for _, managerPerm := range managerRole.Permissions {
|
|
if managerPerm.Name == restrictedPerm {
|
|
return fmt.Errorf("Manager should not have permission '%s'", restrictedPerm)
|
|
}
|
|
}
|
|
}
|
|
|
|
logging.Info("✓ Role system working correctly")
|
|
logging.Info(" - Super Admin role: %d permissions", len(superAdminRole.Permissions))
|
|
logging.Info(" - Admin role: %d permissions", len(adminRole.Permissions))
|
|
logging.Info(" - Manager role: %d permissions", len(managerRole.Permissions))
|
|
|
|
return nil
|
|
}
|
|
|
|
func testSuperAdminDeletionPrevention(db *gorm.DB) error {
|
|
// Create repository and service
|
|
repo := repository.NewMembershipRepository(db)
|
|
service := service.NewMembershipService(repo)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Find the default admin user (should be Super Admin)
|
|
adminUser, err := repo.FindUserByUsername(ctx, "admin")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find admin user: %v", err)
|
|
}
|
|
|
|
// Try to delete the Super Admin user (should fail)
|
|
err = service.DeleteUser(ctx, adminUser.ID)
|
|
if err == nil {
|
|
return fmt.Errorf("deleting Super Admin user should have failed, but it succeeded")
|
|
}
|
|
|
|
if err.Error() != "cannot delete Super Admin user" {
|
|
return fmt.Errorf("expected 'cannot delete Super Admin user' error, got: %v", err)
|
|
}
|
|
|
|
logging.Info("✓ Super Admin deletion prevention working correctly")
|
|
return nil
|
|
}
|
|
|
|
// fileExists checks if a file exists and is not a directory
|
|
func fileExists(filename string) bool {
|
|
info, err := os.Stat(filename)
|
|
if os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
return !info.IsDir()
|
|
}
|