test_oidc_env_reader.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. """BAMBUDDY_OIDC_* reader (#2593).
  2. The reader is deliberately dumb: it maps env vars to field names and applies
  3. defaults. Whether the resulting provider is *valid* is decided later, by the
  4. same OIDCProviderCreate schema the API uses, so env config cannot bypass a
  5. check the UI enforces.
  6. """
  7. from __future__ import annotations
  8. import pytest
  9. from backend.app.core.oidc_env import read_env_oidc_config
  10. REQUIRED = {
  11. "BAMBUDDY_OIDC_NAME": "Keycloak",
  12. "BAMBUDDY_OIDC_ISSUER_URL": "https://sso.example.com/realms/main",
  13. "BAMBUDDY_OIDC_CLIENT_ID": "bambuddy",
  14. "BAMBUDDY_OIDC_CLIENT_SECRET": "s3cr3t",
  15. }
  16. OPTIONAL = (
  17. "BAMBUDDY_OIDC_SCOPES",
  18. "BAMBUDDY_OIDC_ENABLED",
  19. "BAMBUDDY_OIDC_AUTO_CREATE_USERS",
  20. "BAMBUDDY_OIDC_AUTO_LINK_EXISTING",
  21. "BAMBUDDY_OIDC_EMAIL_CLAIM",
  22. "BAMBUDDY_OIDC_REQUIRE_EMAIL_VERIFIED",
  23. "BAMBUDDY_OIDC_ICON_URL",
  24. "BAMBUDDY_OIDC_AUTOLOGIN",
  25. )
  26. @pytest.fixture(autouse=True)
  27. def clean_env(monkeypatch):
  28. for key in (*REQUIRED, *OPTIONAL):
  29. monkeypatch.delenv(key, raising=False)
  30. def _set_required(monkeypatch):
  31. for key, value in REQUIRED.items():
  32. monkeypatch.setenv(key, value)
  33. def test_returns_none_when_nothing_is_configured():
  34. assert read_env_oidc_config() is None
  35. @pytest.mark.parametrize("missing", sorted(REQUIRED))
  36. def test_returns_none_when_any_single_required_var_is_missing(monkeypatch, missing):
  37. """All four or nothing -- a half-configured provider must not reach the
  38. database, where it would fail at authorize time instead of at startup."""
  39. _set_required(monkeypatch)
  40. monkeypatch.delenv(missing)
  41. assert read_env_oidc_config() is None
  42. def test_an_empty_required_var_counts_as_unset(monkeypatch):
  43. """`BAMBUDDY_OIDC_CLIENT_SECRET=` in a compose file is a forgotten value,
  44. not an intentional empty secret."""
  45. _set_required(monkeypatch)
  46. monkeypatch.setenv("BAMBUDDY_OIDC_CLIENT_SECRET", "")
  47. assert read_env_oidc_config() is None
  48. def test_reads_the_required_vars(monkeypatch):
  49. _set_required(monkeypatch)
  50. cfg = read_env_oidc_config()
  51. assert cfg["name"] == "Keycloak"
  52. assert cfg["issuer_url"] == "https://sso.example.com/realms/main"
  53. assert cfg["client_id"] == "bambuddy"
  54. assert cfg["client_secret"] == "s3cr3t"
  55. def test_applies_the_documented_defaults(monkeypatch):
  56. _set_required(monkeypatch)
  57. cfg = read_env_oidc_config()
  58. assert cfg["scopes"] == "openid email profile"
  59. assert cfg["is_enabled"] is True
  60. assert cfg["auto_create_users"] is False
  61. assert cfg["auto_link_existing_accounts"] is False
  62. assert cfg["email_claim"] == "email"
  63. assert cfg["require_email_verified"] is True
  64. assert cfg["icon_url"] is None
  65. assert cfg["is_autologin"] is False
  66. @pytest.mark.parametrize("raw", ["true", "TRUE", "True", "1", "yes", "YES", " yes "])
  67. def test_booleans_accept_the_project_truthy_spellings(monkeypatch, raw):
  68. _set_required(monkeypatch)
  69. monkeypatch.setenv("BAMBUDDY_OIDC_AUTO_CREATE_USERS", raw)
  70. assert read_env_oidc_config()["auto_create_users"] is True
  71. @pytest.mark.parametrize("raw", ["false", "0", "no", "", "off", "nonsense"])
  72. def test_anything_else_is_false(monkeypatch, raw):
  73. """Only the three documented spellings enable a flag; an unrecognised value
  74. must not silently turn on auto-create-users."""
  75. _set_required(monkeypatch)
  76. monkeypatch.setenv("BAMBUDDY_OIDC_AUTO_CREATE_USERS", raw)
  77. assert read_env_oidc_config()["auto_create_users"] is False
  78. def test_a_boolean_default_of_true_can_be_turned_off(monkeypatch):
  79. _set_required(monkeypatch)
  80. monkeypatch.setenv("BAMBUDDY_OIDC_REQUIRE_EMAIL_VERIFIED", "false")
  81. assert read_env_oidc_config()["require_email_verified"] is False
  82. def test_optional_strings_override_their_defaults(monkeypatch):
  83. _set_required(monkeypatch)
  84. monkeypatch.setenv("BAMBUDDY_OIDC_SCOPES", "openid profile groups")
  85. monkeypatch.setenv("BAMBUDDY_OIDC_EMAIL_CLAIM", "mail")
  86. monkeypatch.setenv("BAMBUDDY_OIDC_ICON_URL", "https://sso.example.com/logo.png")
  87. cfg = read_env_oidc_config()
  88. assert cfg["scopes"] == "openid profile groups"
  89. assert cfg["email_claim"] == "mail"
  90. assert cfg["icon_url"] == "https://sso.example.com/logo.png"