test_github_backup_preset_secrets.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. """A printer preset's connection secrets never leave in a Git backup.
  2. A printer connected in the slicer keeps its host's key (for a LAN printer, its
  3. access code) in the preset, and cloud sync carries it. The backup writes those
  4. as the placeholder; cloud profiles are never restored, so nothing reads them
  5. back.
  6. """
  7. import json
  8. from unittest.mock import AsyncMock, MagicMock, patch
  9. import pytest
  10. from backend.app.services.github_backup import GitHubBackupService
  11. from backend.app.services.secret_fields import SECRET_PLACEHOLDER
  12. CONNECTION = {
  13. "print_host": "192.168.1.50",
  14. "printhost_apikey": "value-one",
  15. "printhost_password": "value-two",
  16. "printhost_user": "value-three",
  17. "printer_model": "X1C",
  18. }
  19. MASKED = {
  20. "print_host": "192.168.1.50",
  21. "printhost_apikey": SECRET_PLACEHOLDER,
  22. "printhost_password": SECRET_PLACEHOLDER,
  23. "printhost_user": SECRET_PLACEHOLDER,
  24. "printer_model": "X1C",
  25. }
  26. @pytest.mark.asyncio
  27. async def test_an_orca_printer_preset_keeps_its_connection_secrets_out(db_session):
  28. svc = MagicMock()
  29. svc.list_profiles = AsyncMock(return_value=[{"id": 1, "name": "p", "content": {"type": "machine", **CONNECTION}}])
  30. svc.close = AsyncMock()
  31. files: dict = {}
  32. with patch(
  33. "backend.app.api.routes.orca_cloud._build_authenticated_service", new_callable=AsyncMock, return_value=svc
  34. ):
  35. await GitHubBackupService()._collect_orca_profiles(db_session, files, "global", None)
  36. (path,) = files
  37. assert files[path]["profiles"][0]["content"] == {"type": "machine", **MASKED}
  38. assert "value-" not in json.dumps(files)
  39. @pytest.mark.asyncio
  40. async def test_a_bambu_printer_preset_keeps_its_connection_secrets_out(db_session):
  41. cloud = MagicMock()
  42. cloud.is_authenticated = True
  43. cloud.get_slicer_settings = AsyncMock(
  44. return_value={"printer": {"private": [{"setting_id": "PMUS1", "name": "p", "version": "1"}], "public": []}}
  45. )
  46. cloud.get_setting_detail = AsyncMock(
  47. return_value={"setting_id": "PMUS1", "name": "p", "base_id": "GM001", "setting": dict(CONNECTION)}
  48. )
  49. cloud.close = AsyncMock()
  50. files: dict = {}
  51. with patch("backend.app.api.routes.cloud.build_authenticated_cloud", new_callable=AsyncMock, return_value=cloud):
  52. await GitHubBackupService()._collect_bambu_profiles(db_session, files, "global", None)
  53. assert files["cloud_profiles/bambu/global/printer.json"]["profiles"][0]["setting"] == MASKED
  54. assert "value-" not in json.dumps(files)
  55. @pytest.mark.asyncio
  56. async def test_an_empty_connection_secret_stays_empty(db_session):
  57. svc = MagicMock()
  58. svc.list_profiles = AsyncMock(return_value=[{"id": 1, "name": "p", "content": {"printhost_apikey": ""}}])
  59. svc.close = AsyncMock()
  60. files: dict = {}
  61. with patch(
  62. "backend.app.api.routes.orca_cloud._build_authenticated_service", new_callable=AsyncMock, return_value=svc
  63. ):
  64. await GitHubBackupService()._collect_orca_profiles(db_session, files, "global", None)
  65. (path,) = files
  66. assert files[path]["profiles"][0]["content"] == {"printhost_apikey": ""}
  67. @pytest.mark.asyncio
  68. async def test_a_login_in_a_preset_url_is_masked(db_session):
  69. content = {
  70. "print_host": "http://pi:value-four@octopi.local",
  71. "print_host_webui": "https://value-five@octopi.local/ui",
  72. "printer_settings_id": "http://octopi.local",
  73. "post_process": ["http://u:value-six@octopi.local/hook", "plain"],
  74. }
  75. cloud = MagicMock()
  76. cloud.is_authenticated = True
  77. cloud.get_slicer_settings = AsyncMock(
  78. return_value={"printer": {"private": [{"setting_id": "PMUS1", "name": "p", "version": "1"}], "public": []}}
  79. )
  80. cloud.get_setting_detail = AsyncMock(
  81. return_value={"setting_id": "PMUS1", "name": "p", "base_id": "GM001", "setting": dict(content)}
  82. )
  83. cloud.close = AsyncMock()
  84. svc = MagicMock()
  85. svc.list_profiles = AsyncMock(return_value=[{"id": 1, "name": "p", "content": dict(content)}])
  86. svc.close = AsyncMock()
  87. files: dict = {}
  88. with (
  89. patch("backend.app.api.routes.cloud.build_authenticated_cloud", new_callable=AsyncMock, return_value=cloud),
  90. patch(
  91. "backend.app.api.routes.orca_cloud._build_authenticated_service", new_callable=AsyncMock, return_value=svc
  92. ),
  93. ):
  94. await GitHubBackupService()._collect_bambu_profiles(db_session, files, "global", None)
  95. await GitHubBackupService()._collect_orca_profiles(db_session, files, "global", None)
  96. masked = {
  97. "print_host": "http://pi:[REDACTED]@octopi.local",
  98. "print_host_webui": "https://[REDACTED]@octopi.local/ui",
  99. "printer_settings_id": "http://octopi.local",
  100. "post_process": ["http://u:[REDACTED]@octopi.local/hook", "plain"],
  101. }
  102. bambu = files["cloud_profiles/bambu/global/printer.json"]["profiles"][0]["setting"]
  103. (orca_path,) = [path for path in files if path.startswith("cloud_profiles/orca/")]
  104. assert bambu == masked
  105. assert files[orca_path]["profiles"][0]["content"] == masked