row_visibility.py 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. """Which owned rows (archives, queue jobs, library files) a caller reaches through something that holds them."""
  2. from backend.app.core.auth import ApiKeyActor
  3. from backend.app.core.permissions import Permission
  4. from backend.app.core.printer_scope import PrinterScope
  5. from backend.app.models.archive import PrintArchive
  6. from backend.app.models.print_queue import PrintQueueItem
  7. from backend.app.models.user import User
  8. def owned_row_clauses(
  9. model, actor: User | ApiKeyActor | None, all_permission: Permission, own_permission: Permission
  10. ) -> list | None:
  11. """WHERE clauses for the rows of ``model`` the caller may reach by ownership, or None for none.
  12. Their own unless they may reach everyone's (a row without an owner then
  13. stays out). Auth off (no actor) reaches all.
  14. """
  15. if actor is None:
  16. return []
  17. # A key acts with its owner's rights to these rows, not with its own scope
  18. # flags (a project key needs no archive or queue flag, #1893), so it never
  19. # reaches a row its owner could not. A legacy key without an owner keeps
  20. # every row.
  21. user = actor.owner if isinstance(actor, ApiKeyActor) else actor
  22. if user is None or user.has_permission(all_permission.value):
  23. return []
  24. if user.has_permission(own_permission.value):
  25. return [model.created_by_id == user.id]
  26. return None
  27. def visible_row_clauses(
  28. model: type[PrintArchive] | type[PrintQueueItem],
  29. printer_scope: PrinterScope,
  30. actor: User | ApiKeyActor | None,
  31. all_permission: Permission,
  32. own_permission: Permission,
  33. ) -> list | None:
  34. """WHERE clauses for the archive or queue rows the caller may reach, or None for none.
  35. The rows the caller reaches through the archives or the queue themselves:
  36. on their printers, and by ``owned_row_clauses``.
  37. """
  38. owned = owned_row_clauses(model, actor, all_permission, own_permission)
  39. if owned is None:
  40. return None
  41. if (clause := printer_scope.where(model.printer_id)) is not None:
  42. return [clause, *owned]
  43. return owned
  44. def may_reach_row(
  45. created_by_id: int | None,
  46. actor: User | ApiKeyActor | None,
  47. all_permission: Permission,
  48. own_permission: Permission,
  49. ) -> bool:
  50. """Whether the caller may reach one row owned by ``created_by_id``, printer scope aside.
  51. The rule of ``visible_row_clauses`` for a row already loaded.
  52. """
  53. if actor is None:
  54. return True
  55. user = actor.owner if isinstance(actor, ApiKeyActor) else actor
  56. if user is None or user.has_permission(all_permission.value):
  57. return True
  58. return user.has_permission(own_permission.value) and created_by_id is not None and created_by_id == user.id