Skip to content

Commit 3d6c94a

Browse files
committed
fix(sdk): work around msgraph SDK transferMethods deserialization bug
The Microsoft Graph Python SDK (via kiota-serialization-json) incorrectly deserializes transferMethods in ConditionalAccessAuthenticationFlows, always returning an empty list. This is caused by get_collection_of_enum_values not handling CSV-serialized flags enums (microsoft/kiota-python#515). Fetch raw JSON and parse transferMethods manually as a workaround until the upstream fix (microsoft/kiota-python#516) is released.
1 parent cb88e34 commit 3d6c94a

1 file changed

Lines changed: 70 additions & 16 deletions

File tree

prowler/providers/m365/services/entra/entra_service.py

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,18 @@ async def _get_conditional_access_policies(self):
172172
conditional_access_policies_list = (
173173
await self.client.identity.conditional_access.policies.get()
174174
)
175+
176+
# TODO: Remove this workaround once microsoft/kiota-python#515 is
177+
# fixed and a new version of microsoft-kiota-serialization-json is
178+
# released (see PR microsoft/kiota-python#516). At that point, use
179+
# the SDK's native deserialization for authentication_flows instead.
180+
#
181+
# The SDK deserializer uses get_collection_of_enum_values for
182+
# transferMethods, but the Graph API returns it as a single string
183+
# (e.g., "deviceCodeFlow"), causing the SDK to return an empty list.
184+
# We fetch the raw JSON to correctly parse transferMethods.
185+
raw_auth_flows_map = await self._get_raw_authentication_flows()
186+
175187
for policy in conditional_access_policies_list.value:
176188
conditional_access_policies[policy.id] = ConditionalAccessPolicy(
177189
id=policy.id,
@@ -302,11 +314,7 @@ async def _get_conditional_access_policies(self):
302314
],
303315
),
304316
authentication_flows=self._parse_authentication_flows(
305-
getattr(
306-
policy.conditions,
307-
"authentication_flows",
308-
None,
309-
)
317+
raw_auth_flows_map.get(policy.id)
310318
),
311319
),
312320
grant_controls=GrantControls(
@@ -453,12 +461,55 @@ async def _get_default_app_management_policy(self):
453461
)
454462
return default_app_management_policy
455463

464+
async def _get_raw_authentication_flows(self) -> dict:
465+
"""Fetch authentication flows from the Graph API using a raw JSON request.
466+
467+
TODO: Remove this method once microsoft/kiota-python#515 is fixed and
468+
a new version of microsoft-kiota-serialization-json is released
469+
(see PR microsoft/kiota-python#516). At that point, revert to using
470+
the SDK's native deserialization via policy.conditions.authentication_flows.
471+
472+
The SDK deserializer incorrectly handles the transferMethods field
473+
(uses get_collection_of_enum_values for a single string value),
474+
so we fetch the raw JSON to correctly parse it.
475+
476+
Returns:
477+
A dict mapping policy ID to the raw authenticationFlows dict.
478+
"""
479+
auth_flows_map = {}
480+
try:
481+
request_info = (
482+
self.client.identity.conditional_access.policies.to_get_request_information()
483+
)
484+
request_info.headers.try_add("Prefer", "include-unknown-enum-members")
485+
response = await self.client.request_adapter.send_primitive_async(
486+
request_info, "bytes", {}
487+
)
488+
if response:
489+
data = json.loads(response)
490+
for policy in data.get("value", []):
491+
policy_id = policy.get("id")
492+
auth_flows = (
493+
policy.get("conditions", {}).get("authenticationFlows")
494+
)
495+
if policy_id and auth_flows:
496+
auth_flows_map[policy_id] = auth_flows
497+
except Exception as error:
498+
logger.error(
499+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
500+
)
501+
return auth_flows_map
502+
456503
@staticmethod
457504
def _parse_authentication_flows(auth_flows) -> "AuthenticationFlows | None":
458-
"""Parse authentication flows conditions from the Graph API response.
505+
"""Parse authentication flows from a raw JSON dict.
506+
507+
TODO: Remove this method once microsoft/kiota-python#515 is fixed and
508+
revert to parsing the SDK's ConditionalAccessAuthenticationFlows object
509+
directly (see PR microsoft/kiota-python#516).
459510
460511
Args:
461-
auth_flows: The authentication flows object from the Graph API.
512+
auth_flows: A dict from the raw JSON response (e.g., {"transferMethods": "deviceCodeFlow"}).
462513
463514
Returns:
464515
AuthenticationFlows object or None if not present.
@@ -467,15 +518,18 @@ def _parse_authentication_flows(auth_flows) -> "AuthenticationFlows | None":
467518
return None
468519

469520
transfer_methods = []
470-
raw_methods = getattr(auth_flows, "transfer_methods", None) or []
471-
for method in raw_methods:
472-
method_value = method.value if hasattr(method, "value") else str(method)
473-
try:
474-
transfer_methods.append(TransferMethod(method_value))
475-
except ValueError:
476-
logger.warning(
477-
f"Unknown authentication flow transfer method: {method_value}"
478-
)
521+
raw_value = auth_flows.get("transferMethods")
522+
if raw_value:
523+
# The API may return a single string or a comma-separated value
524+
methods = raw_value.split(",") if isinstance(raw_value, str) else raw_value
525+
for method_str in methods:
526+
method_str = method_str.strip()
527+
try:
528+
transfer_methods.append(TransferMethod(method_str))
529+
except ValueError:
530+
logger.warning(
531+
f"Unknown authentication flow transfer method: {method_str}"
532+
)
479533

480534
return AuthenticationFlows(transfer_methods=transfer_methods)
481535

0 commit comments

Comments
 (0)