Skip to content

Commit 59ab28f

Browse files
committed
Add a doc to the User Guide for combining GAREs
This document covers, in brief, why combining GAREs is both desirable and not safe in many circumstances. It also offers an example of a merge algorithm which can be used in limited cases to simplify a list of GAREs into a smaller list, including a "full demo" run in the `__main__` block.
1 parent 8960271 commit 59ab28f

File tree

3 files changed

+425
-0
lines changed

3 files changed

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

0 commit comments

Comments
 (0)