test_task_tokens.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import pytest
  2. from app.runner.auth import (
  3. TaskTokenExpired,
  4. TaskTokenInvalid,
  5. TaskTokenIssuer,
  6. TaskTokenVerifier,
  7. node_digest,
  8. )
  9. NODE = {
  10. "id": "read_orders",
  11. "type": "sql.query",
  12. "data_source_uid": "01900000-0000-7000-8000-000000000010",
  13. "purpose": "read",
  14. "config": {
  15. "statement": "SELECT * FROM orders WHERE day = :day",
  16. "parameters": {"day": "${parameters.day}"},
  17. },
  18. }
  19. def test_task_token_is_short_lived_and_bound_to_one_node():
  20. clock = lambda: 1_000
  21. issuer = TaskTokenIssuer("x" * 32, clock=clock, ttl_seconds=60)
  22. verifier = TaskTokenVerifier("x" * 32, clock=clock)
  23. token = issuer.issue(
  24. task_uid="01900000-0000-7000-8000-000000000011",
  25. dataflow_uid="01900000-0000-7000-8000-000000000012",
  26. workflow_version=7,
  27. correlation_id="01900000-0000-7000-8000-000000000013",
  28. node=NODE,
  29. )
  30. claims = verifier.verify(token, node=NODE)
  31. assert claims.node_id == "read_orders"
  32. assert claims.node_type == "sql.query"
  33. assert claims.purpose == "read"
  34. assert claims.node_digest == node_digest(NODE)
  35. assert claims.expires_at == 1_060
  36. assert claims.jti
  37. assert "password" not in token.lower()
  38. def test_task_token_rejects_expiry_tampering_and_other_node():
  39. now = [1_000]
  40. issuer = TaskTokenIssuer("x" * 32, clock=lambda: now[0], ttl_seconds=10)
  41. verifier = TaskTokenVerifier("x" * 32, clock=lambda: now[0])
  42. token = issuer.issue(
  43. task_uid="01900000-0000-7000-8000-000000000011",
  44. dataflow_uid="01900000-0000-7000-8000-000000000012",
  45. workflow_version=7,
  46. correlation_id="01900000-0000-7000-8000-000000000013",
  47. node=NODE,
  48. )
  49. changed = {**NODE, "config": {"statement": "DELETE FROM orders"}}
  50. with pytest.raises(TaskTokenInvalid, match="node binding"):
  51. verifier.verify(token, node=changed)
  52. with pytest.raises(TaskTokenInvalid):
  53. verifier.verify(token[:-1] + ("a" if token[-1] != "a" else "b"), node=NODE)
  54. now[0] = 1_011
  55. with pytest.raises(TaskTokenExpired):
  56. verifier.verify(token, node=NODE)
  57. def test_task_token_secret_has_a_minimum_strength():
  58. with pytest.raises(ValueError, match="at least 32"):
  59. TaskTokenIssuer("too-short")