mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
The SSO gate handed X-Forwarded-Access-Token to every oauth2-fronted upstream, so each of the 28 gated roles received a full-scope bearer for the signed-in user while exactly one, n8n, decodes it. The token also grows with the realm's registered origins, and LAM's Apache rejects a header field above 8190 bytes (Moodle#0, run 33689701895). Consumers now declare services.sso.oauth2.pass_access_token. Without it oauth2-proxy already answers the auth request without the token, and the gate blanks the upstream header rather than dropping the directive, because nginx inherits proxy_set_header as an array and an unset header would let a client-supplied one through. The new lint keeps declaration and use in step in both directions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
220 lines
7.6 KiB
Python
220 lines
7.6 KiB
Python
"""Unit tests for the ``sso`` lookup plugin.
|
|
|
|
The plugin is a thin wrapper around
|
|
``utils.roles.applications.services.sso.get_sso_config`` — these tests
|
|
focus on the lookup-shape contract (term parsing, want-path selection,
|
|
error paths). Resolver semantics are covered by
|
|
``tests/unit/python/utils/roles/applications/services/test_sso.py``.
|
|
"""
|
|
|
|
import importlib
|
|
import importlib.util
|
|
import sys
|
|
import unittest
|
|
import unittest.mock as mock
|
|
|
|
from ansible.errors import AnsibleError
|
|
|
|
from . import PROJECT_ROOT
|
|
|
|
|
|
def _load_module(rel_path: str, name: str):
|
|
for key in (
|
|
"utils.roles.applications.config",
|
|
"utils.roles.applications",
|
|
"utils",
|
|
"utils.cache.applications",
|
|
"utils.cache",
|
|
"utils.roles.applications.services.sso",
|
|
"utils.roles.applications.services",
|
|
):
|
|
sys.modules.pop(key, None)
|
|
importlib.import_module("utils.roles.applications.config")
|
|
importlib.import_module("utils.cache.applications")
|
|
importlib.import_module("utils.roles.applications.services.sso")
|
|
|
|
path = PROJECT_ROOT / rel_path
|
|
spec = importlib.util.spec_from_file_location(name, str(path))
|
|
mod = importlib.util.module_from_spec(spec)
|
|
assert spec and spec.loader
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
def _apps(
|
|
*,
|
|
enabled=False,
|
|
shared=False,
|
|
flavor=None,
|
|
oauth2_origin_host=None,
|
|
oauth2_origin_port=None,
|
|
oauth2_acl=None,
|
|
oauth2_allowed_groups=None,
|
|
oauth2_pass_access_token=None,
|
|
include_app=True,
|
|
):
|
|
if not include_app:
|
|
return {}
|
|
sso: dict = {"enabled": enabled, "shared": shared}
|
|
if flavor is not None:
|
|
sso["flavor"] = flavor
|
|
oauth2_sub: dict = {}
|
|
if oauth2_origin_host is not None or oauth2_origin_port is not None:
|
|
oauth2_sub["origin"] = {
|
|
**({"host": oauth2_origin_host} if oauth2_origin_host is not None else {}),
|
|
**({"port": oauth2_origin_port} if oauth2_origin_port is not None else {}),
|
|
}
|
|
if oauth2_acl is not None:
|
|
oauth2_sub["acl"] = oauth2_acl
|
|
if oauth2_allowed_groups is not None:
|
|
oauth2_sub["allowed_groups"] = oauth2_allowed_groups
|
|
if oauth2_pass_access_token is not None:
|
|
oauth2_sub["pass_access_token"] = oauth2_pass_access_token
|
|
if oauth2_sub:
|
|
sso["oauth2"] = oauth2_sub
|
|
return {"web-app-x": {"services": {"sso": sso}}}
|
|
|
|
|
|
class SsoLookupTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.mod = _load_module("plugins/lookup/sso.py", "sso")
|
|
|
|
def _run(self, applications, terms):
|
|
lk = self.mod.LookupModule()
|
|
lk._loader = mock.MagicMock()
|
|
with mock.patch.object(self.mod, "lookup_loader") as loader_mock:
|
|
loader_mock.get.return_value = mock.MagicMock(
|
|
run=lambda *_a, **_k: [applications]
|
|
)
|
|
return lk.run(terms, variables={"applications": applications})
|
|
|
|
def test_zero_terms_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self._run(_apps(), [])
|
|
|
|
def test_three_terms_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self._run(_apps(), ["web-app-x", "is_enabled", "extra"])
|
|
|
|
def test_empty_application_id_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self._run(_apps(), [""])
|
|
|
|
def test_unknown_want_path_raises(self):
|
|
with self.assertRaises(AnsibleError) as ctx:
|
|
self._run(_apps(enabled=True), ["web-app-x", "bogus_key"])
|
|
self.assertIn("unknown want_path", str(ctx.exception))
|
|
|
|
def test_default_returns_full_dict(self):
|
|
result = self._run(_apps(enabled=True, flavor="oauth2"), ["web-app-x"])
|
|
self.assertEqual(len(result), 1)
|
|
payload = result[0]
|
|
self.assertEqual(payload["enabled"], True)
|
|
self.assertEqual(payload["flavor"], "oauth2")
|
|
self.assertTrue(payload["is_proxy_gated"])
|
|
|
|
def test_explicit_all_returns_full_dict(self):
|
|
result = self._run(_apps(enabled=True), ["web-app-x", "all"])
|
|
self.assertEqual(len(result), 1)
|
|
self.assertIsInstance(result[0], dict)
|
|
|
|
def test_is_enabled_returns_bool(self):
|
|
result = self._run(_apps(enabled=True), ["web-app-x", "is_enabled"])
|
|
self.assertEqual(result, [True])
|
|
|
|
def test_is_proxy_gated_true_for_oauth2(self):
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2"), ["web-app-x", "is_proxy_gated"]
|
|
)
|
|
self.assertEqual(result, [True])
|
|
|
|
def test_pass_access_token_defaults_to_false(self):
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2"),
|
|
["web-app-x", "oauth2_pass_access_token"],
|
|
)
|
|
self.assertEqual(result, [False])
|
|
|
|
def test_pass_access_token_reads_the_declared_opt_in(self):
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2", oauth2_pass_access_token=True),
|
|
["web-app-x", "oauth2_pass_access_token"],
|
|
)
|
|
self.assertEqual(result, [True])
|
|
|
|
def test_pass_access_token_accepts_a_rendered_string(self):
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2", oauth2_pass_access_token="True"),
|
|
["web-app-x", "oauth2_pass_access_token"],
|
|
)
|
|
self.assertEqual(result, [True])
|
|
|
|
def test_pass_access_token_rejects_a_rendered_false_string(self):
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2", oauth2_pass_access_token="False"),
|
|
["web-app-x", "oauth2_pass_access_token"],
|
|
)
|
|
self.assertEqual(result, [False])
|
|
|
|
def test_is_proxy_gated_false_for_oidc(self):
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oidc"), ["web-app-x", "is_proxy_gated"]
|
|
)
|
|
self.assertEqual(result, [False])
|
|
|
|
def test_is_proxy_gated_false_when_disabled(self):
|
|
result = self._run(
|
|
_apps(enabled=False, flavor="oauth2"), ["web-app-x", "is_proxy_gated"]
|
|
)
|
|
self.assertEqual(result, [False])
|
|
|
|
def test_flavor_returns_string(self):
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2"), ["web-app-x", "flavor"]
|
|
)
|
|
self.assertEqual(result, ["oauth2"])
|
|
|
|
def test_oauth2_origin_host_returns_value(self):
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2", oauth2_origin_host="application"),
|
|
["web-app-x", "oauth2_origin_host"],
|
|
)
|
|
self.assertEqual(result, ["application"])
|
|
|
|
def test_oauth2_origin_port_stringified(self):
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2", oauth2_origin_port=8000),
|
|
["web-app-x", "oauth2_origin_port"],
|
|
)
|
|
self.assertEqual(result, ["8000"])
|
|
|
|
def test_oauth2_acl_returns_dict(self):
|
|
acl = {"whitelist": ["/api/", "/static/"]}
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2", oauth2_acl=acl),
|
|
["web-app-x", "oauth2_acl"],
|
|
)
|
|
self.assertEqual(result, [acl])
|
|
|
|
def test_oauth2_allowed_groups_returns_list(self):
|
|
groups = ["/roles/web-app-x/admin"]
|
|
result = self._run(
|
|
_apps(enabled=True, flavor="oauth2", oauth2_allowed_groups=groups),
|
|
["web-app-x", "oauth2_allowed_groups"],
|
|
)
|
|
self.assertEqual(result, [groups])
|
|
|
|
def test_missing_app_returns_defaults(self):
|
|
result = self._run({}, ["web-app-x", "is_enabled"])
|
|
self.assertEqual(result, [False])
|
|
|
|
def test_empty_want_path_treated_as_all(self):
|
|
result = self._run(_apps(enabled=True), ["web-app-x", " "])
|
|
self.assertEqual(len(result), 1)
|
|
self.assertIsInstance(result[0], dict)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|