test_permission_backfills_once_3238.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. """Group permission backfills run once, not on every start (#3238).
  2. They used to add their permission to every matching group on each start, so
  3. an admin who took MakerWorld (or clear plate, forecasting, pipelines) away
  4. from a group got it back after a restart.
  5. """
  6. import pytest
  7. from httpx import AsyncClient
  8. from sqlalchemy import select
  9. from backend.app.core import database as _database_module
  10. from backend.app.core.database import seed_default_groups
  11. from backend.app.models.group import Group
  12. from backend.app.models.settings import Settings
  13. # (flag, permission the Administrators group has once a version with the
  14. # backfill started, group permissions that earn it, permission it adds)
  15. BACKFILLS = [
  16. pytest.param(
  17. "_backfill_446_clear_plate_permission_done",
  18. "printers:clear_plate",
  19. ["printers:control"],
  20. "printers:clear_plate",
  21. id="clear_plate",
  22. ),
  23. pytest.param(
  24. "_backfill_1099_makerworld_permissions_done",
  25. "makerworld:view",
  26. ["library:upload"],
  27. "makerworld:import",
  28. id="makerworld",
  29. ),
  30. pytest.param(
  31. "_backfill_1184_forecast_permissions_done",
  32. "inventory:forecast_read",
  33. ["inventory:read"],
  34. "inventory:forecast_read",
  35. id="forecast",
  36. ),
  37. pytest.param(
  38. "_backfill_1425_pipeline_permissions_done",
  39. "pipelines:read",
  40. ["settings:read"],
  41. "pipelines:read",
  42. id="pipelines",
  43. ),
  44. ]
  45. async def _set_group(name: str, permissions: list[str]) -> None:
  46. async with _database_module.async_session() as session:
  47. group = (await session.execute(select(Group).where(Group.name == name))).scalar_one_or_none()
  48. if group is None:
  49. session.add(Group(name=name, permissions=permissions, is_system=False))
  50. else:
  51. group.permissions = permissions
  52. await session.commit()
  53. async def _perms(name: str) -> set[str]:
  54. async with _database_module.async_session() as session:
  55. group = (await session.execute(select(Group).where(Group.name == name))).scalar_one()
  56. return set(group.permissions or [])
  57. async def _upgrade_from(flag: str, marker: str, *, knew_it: bool) -> None:
  58. """Make the next start an upgrade from a version without the flag.
  59. ``knew_it`` is whether that version already had the permission, which
  60. leaves ``marker`` on Administrators.
  61. """
  62. async with _database_module.async_session() as session:
  63. row = (await session.execute(select(Settings).where(Settings.key == flag))).scalar_one_or_none()
  64. if row is not None:
  65. await session.delete(row)
  66. admin = (await session.execute(select(Group).where(Group.name == "Administrators"))).scalar_one()
  67. perms = [p for p in admin.permissions if p != marker]
  68. admin.permissions = [*perms, marker] if knew_it else perms
  69. await session.commit()
  70. async def _flag_set(flag: str) -> bool:
  71. async with _database_module.async_session() as session:
  72. return (await session.execute(select(Settings).where(Settings.key == flag))).scalar_one_or_none() is not None
  73. @pytest.mark.asyncio
  74. @pytest.mark.integration
  75. async def test_makerworld_stays_off_after_restart(async_client: AsyncClient):
  76. """The report: MakerWorld turned off for a group with library:upload."""
  77. await _set_group("student", ["library:read_own", "library:upload"])
  78. await seed_default_groups()
  79. await seed_default_groups()
  80. assert "makerworld:view" not in await _perms("student")
  81. assert "makerworld:import" not in await _perms("student")
  82. @pytest.mark.asyncio
  83. @pytest.mark.integration
  84. @pytest.mark.parametrize(("flag", "marker", "earns", "added"), BACKFILLS)
  85. async def test_removed_permission_stays_removed(async_client: AsyncClient, flag, marker, earns, added):
  86. await _set_group("custom", earns)
  87. await seed_default_groups()
  88. await seed_default_groups()
  89. assert added not in await _perms("custom")
  90. assert await _flag_set(flag)
  91. @pytest.mark.asyncio
  92. @pytest.mark.integration
  93. @pytest.mark.parametrize(("flag", "marker", "earns", "added"), BACKFILLS)
  94. async def test_upgrade_from_a_version_that_already_ran_it(async_client: AsyncClient, flag, marker, earns, added):
  95. """Before #3238 the backfill ran on every start; the start that adds the
  96. flag must not hand back what an admin removed since."""
  97. await _set_group("custom", earns)
  98. await _upgrade_from(flag, marker, knew_it=True)
  99. await seed_default_groups()
  100. assert added not in await _perms("custom")
  101. assert await _flag_set(flag)
  102. @pytest.mark.asyncio
  103. @pytest.mark.integration
  104. @pytest.mark.parametrize(("flag", "marker", "earns", "added"), BACKFILLS)
  105. async def test_upgrade_from_before_the_permission(async_client: AsyncClient, flag, marker, earns, added):
  106. """The backfill still runs on an install that never had the permission,
  107. including those after the Administrators sync, which adds the permission
  108. to Administrators on that same start."""
  109. await _set_group("custom", earns)
  110. await _upgrade_from(flag, marker, knew_it=False)
  111. await seed_default_groups()
  112. assert added in await _perms("custom")
  113. # Taken away again, it stays away.
  114. await _set_group("custom", earns)
  115. await seed_default_groups()
  116. assert added not in await _perms("custom")
  117. @pytest.mark.asyncio
  118. @pytest.mark.integration
  119. async def test_administrators_still_get_every_permission(async_client: AsyncClient):
  120. """Administrators are synced on every start; that is not a backfill."""
  121. await seed_default_groups()
  122. await _set_group("Administrators", ["settings:read"])
  123. await seed_default_groups()
  124. assert {"makerworld:view", "makerworld:import", "printers:clear_plate"} <= await _perms("Administrators")