feat: add self-enrolling Windows Agent package
This commit is contained in:
1 parent
b9b5e5e36f
commit
abf5654a25
20 files changed
+1834
-42
No files matched your search
@@ -0,0 +1,198 @@
|
||||
"""Tests for downloadable Windows Agent enrollment packages."""
|
||||
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.agent_enrollment.router import router
|
||||
from app.agent_packages.windows import (
|
||||
AgentWindowsPackageError,
|
||||
AgentWindowsPackageSourceError,
|
||||
WINDOWS_PACKAGE_FILES,
|
||||
WINDOWS_PACKAGE_ROOT,
|
||||
build_windows_agent_package,
|
||||
normalize_hub_url,
|
||||
)
|
||||
from app.identity import get_current_user, required_api_permission
|
||||
|
||||
|
||||
TOKEN = "t" * 43
|
||||
|
||||
|
||||
class WindowsPackageBuilderTests(unittest.TestCase):
|
||||
def source_tree(self, root: Path):
|
||||
for name in WINDOWS_PACKAGE_FILES:
|
||||
(root / name).write_text(f"source:{name}\n")
|
||||
|
||||
def test_package_contains_only_generic_source_plus_bootstrap_material(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
self.source_tree(root)
|
||||
|
||||
package = build_windows_agent_package(
|
||||
enrollment_token=TOKEN,
|
||||
hub_url="https://minerops.example.test",
|
||||
source_root=root,
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(io.BytesIO(package)) as archive:
|
||||
names = set(archive.namelist())
|
||||
|
||||
expected = {
|
||||
f"{WINDOWS_PACKAGE_ROOT}/{name}"
|
||||
for name in WINDOWS_PACKAGE_FILES
|
||||
}
|
||||
|
||||
expected.update(
|
||||
{
|
||||
f"{WINDOWS_PACKAGE_ROOT}/enrollment.json",
|
||||
f"{WINDOWS_PACKAGE_ROOT}/INSTALL.txt",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertEqual(names, expected)
|
||||
|
||||
enrollment = json.loads(
|
||||
archive.read(
|
||||
f"{WINDOWS_PACKAGE_ROOT}/enrollment.json"
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(enrollment["token"], TOKEN)
|
||||
self.assertEqual(
|
||||
enrollment["hub_url"],
|
||||
"https://minerops.example.test",
|
||||
)
|
||||
|
||||
serialized = json.dumps(enrollment)
|
||||
|
||||
self.assertNotIn("agent_secret", serialized)
|
||||
self.assertNotIn("api3_password", serialized)
|
||||
self.assertNotIn("api3_account", serialized)
|
||||
self.assertNotIn("agent-config.json", "\n".join(names))
|
||||
|
||||
def test_missing_canonical_source_fails_closed(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
self.source_tree(root)
|
||||
|
||||
(root / WINDOWS_PACKAGE_FILES[0]).unlink()
|
||||
|
||||
with self.assertRaises(AgentWindowsPackageSourceError):
|
||||
build_windows_agent_package(
|
||||
enrollment_token=TOKEN,
|
||||
hub_url="https://minerops.example.test",
|
||||
source_root=root,
|
||||
)
|
||||
|
||||
def test_hub_url_is_origin_only(self):
|
||||
self.assertEqual(
|
||||
normalize_hub_url("https://minerops.example.test/"),
|
||||
"https://minerops.example.test",
|
||||
)
|
||||
|
||||
for invalid in (
|
||||
"ftp://minerops.example.test",
|
||||
"https://user:pass@minerops.example.test",
|
||||
"https://minerops.example.test/path",
|
||||
"https://minerops.example.test/?token=x",
|
||||
):
|
||||
with self.subTest(value=invalid):
|
||||
with self.assertRaises(AgentWindowsPackageError):
|
||||
normalize_hub_url(invalid)
|
||||
|
||||
|
||||
class WindowsPackageRouteTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.app = FastAPI()
|
||||
self.app.include_router(router)
|
||||
self.client = TestClient(self.app)
|
||||
|
||||
def tearDown(self):
|
||||
self.client.close()
|
||||
|
||||
def authenticate(self, permissions):
|
||||
self.app.dependency_overrides[get_current_user] = lambda: {
|
||||
"id": "00000000-0000-4000-8000-000000009100",
|
||||
"username": "package-test",
|
||||
"display_name": "Package Test",
|
||||
"role": "Test",
|
||||
"permissions": tuple(permissions),
|
||||
}
|
||||
|
||||
def test_permission_mapping_is_manage_only(self):
|
||||
self.assertEqual(
|
||||
required_api_permission(
|
||||
"POST",
|
||||
"/api/agent-enrollments/windows-package",
|
||||
),
|
||||
"agents.manage",
|
||||
)
|
||||
|
||||
def test_view_only_user_cannot_download_package(self):
|
||||
self.authenticate(("agents.view",))
|
||||
|
||||
response = self.client.post(
|
||||
"/api/agent-enrollments/windows-package",
|
||||
headers={"Origin": "http://testserver"},
|
||||
json={
|
||||
"token": TOKEN,
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_manager_download_is_same_origin_and_not_cacheable(self):
|
||||
self.authenticate(("agents.view", "agents.manage"))
|
||||
|
||||
response = self.client.post(
|
||||
"/api/agent-enrollments/windows-package",
|
||||
headers={"Origin": "http://testserver"},
|
||||
json={
|
||||
"token": TOKEN,
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(
|
||||
response.headers["content-type"],
|
||||
"application/zip",
|
||||
)
|
||||
self.assertEqual(
|
||||
response.headers["cache-control"],
|
||||
"no-store",
|
||||
)
|
||||
self.assertIn(
|
||||
'filename="MinerOpsAgent-Windows.zip"',
|
||||
response.headers["content-disposition"],
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(io.BytesIO(response.content)) as archive:
|
||||
enrollment = json.loads(
|
||||
archive.read(
|
||||
f"{WINDOWS_PACKAGE_ROOT}/enrollment.json"
|
||||
)
|
||||
)
|
||||
self.assertEqual(enrollment["token"], TOKEN)
|
||||
|
||||
def test_package_requires_browser_origin(self):
|
||||
self.authenticate(("agents.view", "agents.manage"))
|
||||
|
||||
response = self.client.post(
|
||||
"/api/agent-enrollments/windows-package",
|
||||
json={
|
||||
"token": TOKEN,
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user