Skip to content

Commit 5b15474

Browse files
chore: improve performances in eligibility listing (#9)
* chore: improve performances in eligibility listing * fix: bump tag
1 parent 02a5079 commit 5b15474

3 files changed

Lines changed: 116 additions & 21 deletions

File tree

Formula/team-cli.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ class TeamCli < Formula
33

44
desc "CLI for AWS TEAM (Temporary Elevated Access Management)"
55
homepage "https://github.com/DocPlanner/team-cli"
6-
url "https://github.com/DocPlanner/team-cli.git", tag: "v0.3.2"
7-
version "0.3.2"
6+
url "https://github.com/DocPlanner/team-cli.git", tag: "v0.3.3"
7+
version "0.3.3"
88
license "MIT"
99

1010

team_cli/api.py

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -78,38 +78,64 @@ def get_user_policy(tokens: dict) -> dict:
7878
7979
The TEAM backend getUserPolicy is async (fires a Lambda and returns immediately
8080
with policy: null). The frontend catches the result via GraphQL subscription.
81-
Instead, we compute the policy directly: look up each group's eligibility,
82-
resolve OUs to accounts, and build the combined policy.
81+
Instead, we compute the policy directly: list eligibilities once, filter by
82+
the user's identities, resolve OUs to accounts, and build the combined policy.
8383
"""
8484
from team_cli.queries import GET_ELIGIBILITY, GET_OU_ACCOUNTS
8585
user = get_user_info(tokens)
86+
identity_ids = _identity_ids(user)
87+
88+
try:
89+
eligibilities = _list_matching_eligibilities(tokens, identity_ids)
90+
except AuthExpiredError:
91+
raise
92+
except APIError:
93+
eligibilities = _get_matching_eligibilities_by_id(tokens, identity_ids, GET_ELIGIBILITY)
94+
95+
eligibility_by_id = {
96+
eligibility.get("id"): eligibility
97+
for eligibility in eligibilities
98+
if eligibility and eligibility.get("id")
99+
}
100+
ordered_eligibilities = [
101+
eligibility_by_id[identity_id]
102+
for identity_id in identity_ids
103+
if identity_id in eligibility_by_id
104+
]
105+
106+
ou_ids = []
107+
seen_ou_ids = set()
108+
for eligibility in ordered_eligibilities:
109+
for ou in eligibility.get("ous") or []:
110+
ou_id = ou.get("id")
111+
if ou_id and ou_id not in seen_ou_ids:
112+
seen_ou_ids.add(ou_id)
113+
ou_ids.append(ou_id)
114+
115+
ou_account_map = {}
116+
if ou_ids:
117+
ou_data = execute(GET_OU_ACCOUNTS, {"ouIds": ou_ids}, tokens)
118+
for result in (ou_data.get("getOUAccounts") or {}).get("results") or []:
119+
ou_id = result.get("ouId")
120+
if ou_id:
121+
ou_account_map[ou_id] = result.get("accounts") or []
86122

87123
policy_entries = []
88124
max_duration = 0
89125

90-
for gid in [user["user_id"]] + user["group_ids"]:
91-
if not gid:
92-
continue
93-
data = execute(GET_ELIGIBILITY, {"id": gid}, tokens)
94-
elig = data.get("getEligibility")
95-
if not elig:
96-
continue
97-
98-
duration = int(elig.get("duration") or 0)
126+
for eligibility in ordered_eligibilities:
127+
duration = int(eligibility.get("duration") or 0)
99128
if duration > max_duration:
100129
max_duration = duration
101130

102-
accounts = list(elig.get("accounts") or [])
103-
ou_ids = [ou["id"] for ou in (elig.get("ous") or [])]
104-
if ou_ids:
105-
ou_data = execute(GET_OU_ACCOUNTS, {"ouIds": ou_ids}, tokens)
106-
for result in (ou_data.get("getOUAccounts") or {}).get("results") or []:
107-
accounts.extend(result.get("accounts") or [])
131+
accounts = list(eligibility.get("accounts") or [])
132+
for ou in eligibility.get("ous") or []:
133+
accounts.extend(ou_account_map.get(ou.get("id"), []))
108134

109135
policy_entries.append({
110136
"accounts": accounts,
111-
"permissions": elig.get("permissions") or [],
112-
"approvalRequired": elig.get("approvalRequired", False),
137+
"permissions": eligibility.get("permissions") or [],
138+
"approvalRequired": eligibility.get("approvalRequired", False),
113139
"duration": str(max_duration),
114140
})
115141

@@ -120,6 +146,57 @@ def get_user_policy(tokens: dict) -> dict:
120146
}
121147

122148

149+
def _identity_ids(user: dict) -> list[str]:
150+
ids = []
151+
seen = set()
152+
for identity_id in [user.get("user_id")] + list(user.get("group_ids") or []):
153+
if identity_id and identity_id not in seen:
154+
seen.add(identity_id)
155+
ids.append(identity_id)
156+
return ids
157+
158+
159+
def _list_matching_eligibilities(tokens: dict, identity_ids: list[str]) -> list[dict]:
160+
from team_cli.queries import LIST_ELIGIBILITIES
161+
162+
identity_id_set = set(identity_ids)
163+
eligibilities = []
164+
next_token = None
165+
166+
while True:
167+
variables = {"limit": 1000}
168+
if next_token:
169+
variables["nextToken"] = next_token
170+
171+
data = execute(LIST_ELIGIBILITIES, variables, tokens)
172+
result = data.get("listEligibilities") or {}
173+
eligibilities.extend(
174+
eligibility
175+
for eligibility in (result.get("items") or [])
176+
if eligibility and eligibility.get("id") in identity_id_set
177+
)
178+
179+
next_token = result.get("nextToken")
180+
if not next_token:
181+
break
182+
183+
return eligibilities
184+
185+
186+
def _get_matching_eligibilities_by_id(
187+
tokens: dict,
188+
identity_ids: list[str],
189+
query: str,
190+
) -> list[dict]:
191+
eligibilities = []
192+
for identity_id in identity_ids:
193+
data = execute(query, {"id": identity_id}, tokens)
194+
eligibility = data.get("getEligibility")
195+
if eligibility:
196+
eligibilities.append(eligibility)
197+
return eligibilities
198+
199+
123200
def get_requests_by_email(email: str, tokens: dict, status: dict | None = None) -> list:
124201
"""Fetch requests for a given email, with optional status filter. Paginates automatically."""
125202
from team_cli.queries import REQUEST_BY_EMAIL_AND_STATUS

team_cli/queries.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,24 @@
111111
}
112112
"""
113113

114+
LIST_ELIGIBILITIES = """
115+
query ListEligibilities($limit: Int, $nextToken: String) {
116+
listEligibilities(limit: $limit, nextToken: $nextToken) {
117+
items {
118+
id
119+
name
120+
type
121+
duration
122+
approvalRequired
123+
accounts { name id }
124+
permissions { name id }
125+
ous { name id }
126+
}
127+
nextToken
128+
}
129+
}
130+
"""
131+
114132
GET_OU_ACCOUNTS = """
115133
query GetOUAccounts($ouIds: [String]!) {
116134
getOUAccounts(ouIds: $ouIds) {

0 commit comments

Comments
 (0)