//go:build windows

package profile

import (
	"os"
	"path/filepath"
	"testing"
	"unsafe"

	"golang.org/x/sys/windows"
)

const fileAllAccess windows.ACCESS_MASK = windows.STANDARD_RIGHTS_REQUIRED | windows.SYNCHRONIZE | 0x1ff

func TestRuntimeDirWindowsProtectedACL(t *testing.T) {
	isolateRuntimeDirConfig(t)
	testConfigDir := t.TempDir()
	t.Setenv(ConfigDirEnvKey, testConfigDir)

	runtimeDir, err := EnsureRuntimeDir()
	if err != nil {
		t.Fatalf("EnsureRuntimeDir() error = %v", err)
	}
	assertProtectedRuntimeACL(t, runtimeDir, true)
}

func TestSecureRuntimeFileWindowsProtectedACL(t *testing.T) {
	isolateRuntimeDirConfig(t)
	testConfigDir := t.TempDir()
	t.Setenv(ConfigDirEnvKey, testConfigDir)
	runtimeDir, err := EnsureRuntimeDir()
	if err != nil {
		t.Fatalf("EnsureRuntimeDir() error = %v", err)
	}
	path := filepath.Join(runtimeDir, "state")
	if err := os.WriteFile(path, []byte("state"), 0o600); err != nil {
		t.Fatalf("create runtime file: %v", err)
	}

	if err := SecureRuntimeFile(path); err != nil {
		t.Fatalf("SecureRuntimeFile() error = %v", err)
	}
	assertProtectedRuntimeACL(t, path, false)
}

func assertProtectedRuntimeACL(t *testing.T, path string, inheritable bool) {
	t.Helper()

	descriptor, err := windows.GetNamedSecurityInfo(
		path,
		windows.SE_FILE_OBJECT,
		windows.DACL_SECURITY_INFORMATION,
	)
	if err != nil {
		t.Fatalf("GetNamedSecurityInfo(%q): %v", path, err)
	}
	control, _, err := descriptor.Control()
	if err != nil {
		t.Fatalf("read security descriptor control: %v", err)
	}
	if control&windows.SE_DACL_PROTECTED == 0 {
		t.Fatalf("DACL for %q is not protected", path)
	}
	dacl, _, err := descriptor.DACL()
	if err != nil {
		t.Fatalf("read DACL for %q: %v", path, err)
	}

	tokenUser, err := windows.GetCurrentProcessToken().GetTokenUser()
	if err != nil {
		t.Fatalf("get current user SID: %v", err)
	}
	systemSID, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
	if err != nil {
		t.Fatalf("create SYSTEM SID: %v", err)
	}
	type aceCoverage struct {
		self       bool
		objects    bool
		containers bool
	}
	allowed := map[string]aceCoverage{
		tokenUser.User.Sid.String(): {},
		systemSID.String():          {},
	}

	for i := uint32(0); i < uint32(dacl.AceCount); i++ {
		var ace *windows.ACCESS_ALLOWED_ACE
		if err := windows.GetAce(dacl, i, &ace); err != nil {
			t.Fatalf("GetAce(%d): %v", i, err)
		}
		if ace.Header.AceType != windows.ACCESS_ALLOWED_ACE_TYPE {
			t.Fatalf("ACE %d type = %d, want ACCESS_ALLOWED", i, ace.Header.AceType)
		}
		if ace.Header.AceFlags&windows.INHERITED_ACE != 0 {
			t.Fatalf("ACE %d is inherited", i)
		}
		if inheritable {
			allowedFlags := uint8(windows.OBJECT_INHERIT_ACE | windows.CONTAINER_INHERIT_ACE | windows.INHERIT_ONLY_ACE)
			if unexpected := ace.Header.AceFlags &^ allowedFlags; unexpected != 0 {
				t.Fatalf("ACE %d has unexpected flags %#x", i, unexpected)
			}
		} else if ace.Header.AceFlags != 0 {
			t.Fatalf("ACE %d flags = %#x, want none", i, ace.Header.AceFlags)
		}
		inheritance := ace.Header.AceFlags & (windows.OBJECT_INHERIT_ACE | windows.CONTAINER_INHERIT_ACE | windows.INHERIT_ONLY_ACE)
		if ace.Mask != windows.GENERIC_ALL && ace.Mask != fileAllAccess {
			t.Fatalf("ACE %d mask = %#x, want full control", i, ace.Mask)
		}
		sid := (*windows.SID)(unsafe.Pointer(&ace.SidStart))
		coverage, ok := allowed[sid.String()]
		if !ok {
			t.Fatalf("ACE %d grants unexpected SID %s", i, sid)
		}
		if inheritance&windows.INHERIT_ONLY_ACE == 0 {
			coverage.self = true
		}
		if inheritance&windows.OBJECT_INHERIT_ACE != 0 {
			coverage.objects = true
		}
		if inheritance&windows.CONTAINER_INHERIT_ACE != 0 {
			coverage.containers = true
		}
		allowed[sid.String()] = coverage
	}
	for sid, coverage := range allowed {
		if !coverage.self {
			t.Fatalf("DACL does not grant expected SID %s access to the path", sid)
		}
		if inheritable && (!coverage.objects || !coverage.containers) {
			t.Fatalf("DACL does not grant expected SID %s access to descendants", sid)
		}
	}
}
