Skip to content

Commit 4e8ed0e

Browse files
authored
Add a doc to the User Guide for combining GAREs (#1343)
2 parents 95db611 + b52bd5e commit 4e8ed0e

File tree

3 files changed

+430
-0
lines changed

3 files changed

+430
-0
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import globus_sdk.gare
2+
3+
4+
def coalesce(
5+
*gares: globus_sdk.gare.GARE,
6+
session_message: str | None = None,
7+
prompt: str | None = None,
8+
) -> list[globus_sdk.gare.GARE]:
9+
# build a list of GARE fields which are allowed to merge
10+
safe_fields = ["session_required_policies", "required_scopes"]
11+
if session_message is not None:
12+
safe_fields.append("session_message")
13+
if prompt is not None:
14+
safe_fields.append("prompt")
15+
16+
# Build lists of GAREs that can and cannot be merged
17+
candidates, non_candidates = [], []
18+
for g in gares:
19+
if _is_candidate(g, safe_fields):
20+
candidates.append(g)
21+
else:
22+
non_candidates.append(g)
23+
24+
# if no GAREs were safe to merge, return early
25+
if not candidates:
26+
return non_candidates
27+
28+
# merge safe GAREs and override any provided field values
29+
combined = _safe_combine(candidates)
30+
if session_message is not None:
31+
combined.authorization_parameters.session_message = session_message
32+
if prompt is not None:
33+
combined.authorization_parameters.prompt = prompt
34+
35+
# return the reduced list of GAREs
36+
return [combined] + non_candidates
37+
38+
39+
def _is_candidate(g: globus_sdk.gare.GARE, safe_fields: list[str]) -> bool:
40+
params = g.authorization_parameters
41+
42+
# check all of the supported GARE fields
43+
for field_name in (
44+
"session_message",
45+
"session_required_identities",
46+
"session_required_policies",
47+
"session_required_single_domain",
48+
"session_required_mfa",
49+
"required_scopes",
50+
"prompt",
51+
):
52+
# if the field is considered safe, ignore it
53+
if field_name in safe_fields:
54+
continue
55+
# if the field isn't considered safe and it is set,
56+
# then the GARE shouldn't be merged
57+
if getattr(params, field_name) is not None:
58+
return False
59+
60+
# if we didn't find any invalidating fields, it must be safe to merge
61+
return True
62+
63+
64+
def _safe_combine(mergeable_gares: list[globus_sdk.gare.GARE]) -> globus_sdk.gare.GARE:
65+
code = "AuthorizationRequired"
66+
if all(g.code == "ConsentRequired" for g in mergeable_gares):
67+
code = "ConsentRequired"
68+
69+
combined_params = globus_sdk.gare.GlobusAuthorizationParameters(
70+
session_required_policies=_concat(
71+
[
72+
g.authorization_parameters.session_required_policies
73+
for g in mergeable_gares
74+
]
75+
),
76+
required_scopes=_concat(
77+
[g.authorization_parameters.required_scopes for g in mergeable_gares]
78+
),
79+
)
80+
81+
return globus_sdk.gare.GARE(code=code, authorization_parameters=combined_params)
82+
83+
84+
def _concat(values: list[list[str] | None]) -> list[str] | None:
85+
if all(v is None for v in values):
86+
return None
87+
88+
return [element for value in values if value is not None for element in value]
89+
90+
91+
if __name__ == "__main__":
92+
# these are example errors
93+
case1 = globus_sdk.gare.to_gare(
94+
{
95+
"code": "ConsentRequired",
96+
"authorization_parameters": {"required_scopes": ["foo"]},
97+
}
98+
)
99+
case2 = globus_sdk.gare.to_gare(
100+
{
101+
"code": "ConsentRequired",
102+
"authorization_parameters": {"required_scopes": ["bar"]},
103+
}
104+
)
105+
case3 = globus_sdk.gare.to_gare(
106+
{
107+
"code": "AuthorizationRequired",
108+
"authorization_parameters": {"required_scopes": ["baz"]},
109+
}
110+
)
111+
case4 = globus_sdk.gare.to_gare(
112+
{
113+
"code": "AuthorizationRequired",
114+
"authorization_parameters": {
115+
"session_required_policies": [
116+
"f2047039-2f07-4f13-b21b-b2edf7f9d329",
117+
"2fc6d9a3-9322-48a1-ad39-5dcf63a593a7",
118+
],
119+
},
120+
}
121+
)
122+
case5 = globus_sdk.gare.to_gare(
123+
{
124+
"code": "AuthorizationRequired",
125+
"authorization_parameters": {
126+
"session_required_policies": [
127+
"f2047039-2f07-4f13-b21b-b2edf7f9d329",
128+
"2fc6d9a3-9322-48a1-ad39-5dcf63a593a7",
129+
],
130+
"session_required_mfa": True,
131+
},
132+
}
133+
)
134+
case6 = globus_sdk.gare.to_gare(
135+
{
136+
"code": "AuthorizationRequired",
137+
"authorization_parameters": {
138+
"session_required_policies": ["ba10f6f1-5b23-4703-bfb7-4fdd7b529546"],
139+
"session_message": "needs a policy",
140+
},
141+
}
142+
)
143+
case7 = globus_sdk.gare.to_gare(
144+
{
145+
"code": "AuthorizationRequired",
146+
"authorization_parameters": {
147+
"session_required_policies": ["ba10f6f1-5b23-4703-bfb7-4fdd7b529546"],
148+
"prompt": "login",
149+
},
150+
}
151+
)
152+
153+
print("\n--full merge--\n")
154+
print("\ncombining two:")
155+
for g in coalesce(case1, case2):
156+
print(" -", g)
157+
print("\ncombining three:")
158+
for g in coalesce(case1, case2, case3):
159+
print(" -", g)
160+
print("\ncombining four:")
161+
for g in coalesce(case1, case2, case3, case4):
162+
print(" -", g)
163+
164+
print("\n--no merge--\n")
165+
print("\ncombining two:")
166+
for g in coalesce(case1, case5):
167+
print(" -", g)
168+
print("\ncombining two:")
169+
for g in coalesce(case4, case5):
170+
print(" -", g)
171+
print("\ncombining two:")
172+
for g in coalesce(case2, case6):
173+
print(" -", g)
174+
print("\ncombining two:")
175+
for g in coalesce(case4, case7):
176+
print(" -", g)
177+
178+
print("\n--merge due to explicit param--\n")
179+
print("\ncombining two:")
180+
for g in coalesce(case1, case6, session_message="explicit message"):
181+
print(" -", g)
182+
print("\ncombining two:")
183+
for g in coalesce(case4, case7, prompt="login"):
184+
print(" -", g)

0 commit comments

Comments
 (0)