test_task_tokens.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  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. def clock():
  21. return 1_000
  22. issuer = TaskTokenIssuer("x" * 32, clock=clock, ttl_seconds=60)
  23. verifier = TaskTokenVerifier("x" * 32, clock=clock)
  24. token = issuer.issue(
  25. task_uid="01900000-0000-7000-8000-000000000011",
  26. dataflow_uid="01900000-0000-7000-8000-000000000012",
  27. deployment_id="01900000-0000-7000-8000-000000000014",
  28. environment="test",
  29. workflow_version=7,
  30. correlation_id="01900000-0000-7000-8000-000000000013",
  31. node=NODE,
  32. )
  33. claims = verifier.verify(token, node=NODE)
  34. assert claims.node_id == "read_orders"
  35. assert claims.deployment_id == "01900000-0000-7000-8000-000000000014"
  36. assert claims.environment == "test"
  37. assert claims.node_type == "sql.query"
  38. assert claims.purpose == "read"
  39. assert claims.node_digest == node_digest(NODE)
  40. assert claims.expires_at == 1_060
  41. assert claims.jti
  42. assert "password" not in token.lower()
  43. def test_task_token_rejects_expiry_tampering_and_other_node():
  44. now = [1_000]
  45. issuer = TaskTokenIssuer("x" * 32, clock=lambda: now[0], ttl_seconds=10)
  46. verifier = TaskTokenVerifier("x" * 32, clock=lambda: now[0])
  47. token = issuer.issue(
  48. task_uid="01900000-0000-7000-8000-000000000011",
  49. dataflow_uid="01900000-0000-7000-8000-000000000012",
  50. deployment_id="01900000-0000-7000-8000-000000000014",
  51. environment="test",
  52. workflow_version=7,
  53. correlation_id="01900000-0000-7000-8000-000000000013",
  54. node=NODE,
  55. )
  56. changed = {**NODE, "config": {"statement": "DELETE FROM orders"}}
  57. with pytest.raises(TaskTokenInvalid, match="node binding"):
  58. verifier.verify(token, node=changed)
  59. header, payload, signature = token.split(".")
  60. changed_signature = ("A" if signature[0] != "A" else "B") + signature[1:]
  61. with pytest.raises(TaskTokenInvalid):
  62. verifier.verify(".".join((header, payload, changed_signature)), node=NODE)
  63. now[0] = 1_011
  64. with pytest.raises(TaskTokenExpired):
  65. verifier.verify(token, node=NODE)
  66. def test_task_token_secret_has_a_minimum_strength():
  67. with pytest.raises(ValueError, match="at least 32"):
  68. TaskTokenIssuer("too-short")