Skip to content

Commit 924ff93

Browse files
Merge pull request #81 from famedly/fix/pathlist-early-return
fix: prevent early return in PathList lookup when intermediate path fails
2 parents a19cd65 + d6e3415 commit 924ff93

2 files changed

Lines changed: 60 additions & 3 deletions

File tree

synapse_token_authenticator/utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@ def get_path_in_dict(path: str | List[str] | List[List[str]], d: Any) -> Optiona
6161
r = d
6262
for segment in p:
6363
if not isinstance(r, dict):
64-
return None
64+
break
6565
r = r.get(segment)
66-
if r is not None:
67-
return r
66+
else:
67+
if r is not None:
68+
return r
6869
return None
6970

7071

tests/test_sta_utils.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,62 @@ def test_get_path_in_dict():
3838
assert get_path_in_dict([["foo", "loo"], []], {"foo": {"loo": 3}}) == 3
3939

4040

41+
def test_get_path_in_dict_pathlist_fallback_on_missing_key():
42+
"""When the first path's key is entirely absent, later paths must still be tried."""
43+
assert (
44+
get_path_in_dict([["missing", "sub"], ["foo", "bar"]], {"foo": {"bar": 3}}) == 3
45+
)
46+
assert (
47+
get_path_in_dict([["a", "b"], ["c", "d"], ["e", "f"]], {"e": {"f": 42}}) == 42
48+
)
49+
assert (
50+
get_path_in_dict(
51+
[["missing", "sub"], ["also_missing", "sub"]], {"foo": {"bar": 3}}
52+
)
53+
is None
54+
)
55+
56+
57+
def test_get_path_in_dict_pathlist_non_dict_intermediate():
58+
"""When an intermediate value is a non-dict (e.g. int), later paths must still be tried."""
59+
assert (
60+
get_path_in_dict(
61+
[["foo", "bar"], ["baz", "qux"]], {"foo": 42, "baz": {"qux": 7}}
62+
)
63+
== 7
64+
)
65+
assert (
66+
get_path_in_dict(
67+
[["a", "b", "c"], ["x", "y"]],
68+
{"a": {"b": "not_a_dict"}, "x": {"y": 99}},
69+
)
70+
== 99
71+
)
72+
73+
74+
def test_get_path_in_dict_zitadel_admin_path():
75+
"""Real-world scenario: Zitadel project-scoped role claims with PathList fallback."""
76+
token = {
77+
"urn:zitadel:iam:org:project:12345:roles": {
78+
"MatrixAdmin": {"org_id": "famedly.localhost"}
79+
},
80+
}
81+
assert get_path_in_dict(
82+
[
83+
["roles", "Admin"],
84+
["urn:zitadel:iam:org:project:12345:roles", "MatrixAdmin"],
85+
],
86+
token,
87+
) == {"org_id": "famedly.localhost"}
88+
assert get_path_in_dict(
89+
[
90+
["urn:zitadel:iam:org:project:12345:roles", "MatrixAdmin"],
91+
["roles", "Admin"],
92+
],
93+
token,
94+
) == {"org_id": "famedly.localhost"}
95+
96+
4197
def test_validate_scopes():
4298
assert validate_scopes("foo boo", "boo foo")
4399
assert validate_scopes(["foo", "boo"], "boo foo")

0 commit comments

Comments
 (0)