Skip to content

fix: relax two SDK model gaps blocking list_policies on Okta for Government tenants - #102

Closed
jackvaughanjr wants to merge 1 commit into
okta:mainfrom
jackvaughanjr:fix/policy-model-schema-gaps
Closed

fix: relax two SDK model gaps blocking list_policies on Okta for Government tenants#102
jackvaughanjr wants to merge 1 commit into
okta:mainfrom
jackvaughanjr:fix/policy-model-schema-gaps

Conversation

@jackvaughanjr

@jackvaughanjr jackvaughanjr commented Jul 31, 2026

Copy link
Copy Markdown

Summary

Fixes #100 and #101, two distinct pydantic ValidationErrors that abort list_policies/list_policy_rules entirely, both found while testing against a live Okta for Government (FedRAMP) tenant.

  1. list_policies(type="ACCESS_POLICY") fails: _embedded.resourceType returned as string, Policy model expects dict #100: Policy.embedded is typed Dict[str, Dict[str, Any]], but an ACCESS_POLICY mapped to an app returns _embedded: {"resourceType": "APP"} (a flat string value). Aborts list_policies(type="ACCESS_POLICY") / get_policy for the whole tenant as soon as one policy has this shape.
  2. list_policies(type="MFA_ENROLL") fails on smart_card_idp: enum missing from AuthenticatorEnrollmentPolicyAuthenticatorType #101: AuthenticatorEnrollmentPolicyAuthenticatorType doesn't include smart_card_idp, even though AuthenticatorKeyEnum recognizes it elsewhere in the SDK. Aborts list_policies(type="MFA_ENROLL") for any tenant with a smart-card/PIV-CAC MFA enrollment policy (common in gov tenants, rare commercially).

Fix

Both follow the same pattern as the existing LogSecurityContext.user_behaviors workaround already in system_logs.py: relax the overly strict field annotation and force a Pydantic schema rebuild at import time, rather than waiting on an okta SDK release.

  • Policy.embeddedOptional[Dict[str, Any]], rebuilt on Policy, AccessPolicy, and any other already-loaded Policy subclass explicitly, not just the base class, so the fix isn't sensitive to which module happens to import a given subclass first (pydantic v2 subclasses cache their own core schema at class-definition time).
  • AuthenticatorEnrollmentPolicyAuthenticatorSettings.keyOptional[str], so any authenticator key Okta returns is accepted instead of maintaining a second, hand-kept enum that can drift from AuthenticatorKeyEnum.

Verification

Confirmed against a live Okta for Government tenant, before and after this change:

Call Before After
list_policies(type="ACCESS_POLICY") ValidationError: _embedded.resourceType 14 policies returned
list_policies(type="MFA_ENROLL") ValidationError: key ... 'smart_card_idp' 2 policies returned
list_policy_rules(<rst... access policy id>) n/a (blocked by #100) 2 rules returned

PASSWORD and OKTA_SIGN_ON policies were unaffected before and after, confirming the bug is specific to these two shapes, not a general policy-parsing regression.

Tests

New tests/test_policy_model_workarounds.py: both workarounds, plus backward-compatibility cases confirming previously-valid shapes (nested-dict _embedded values, already-valid enum keys) still validate identically. Full suite: 535 passed.

…rnment tenants

Two distinct, reproducible pydantic ValidationErrors abort list_policies/list_policy_rules
entirely, observed against a live Okta for Government (FedRAMP) tenant:

1. `Policy.embedded` is typed `Dict[str, Dict[str, Any]]`, but an ACCESS_POLICY mapped to
   an app returns `_embedded: {"resourceType": "APP"}` — a flat string value, not a nested
   object. This aborts `list_policies(type="ACCESS_POLICY")` and `get_policy` for every
   policy in the tenant as soon as one carries this shape.

2. `AuthenticatorEnrollmentPolicyAuthenticatorType` (used by MFA_ENROLL policy authenticator
   settings) omits `smart_card_idp`, even though the SDK's own `AuthenticatorKeyEnum`
   recognizes it elsewhere. Smart-card/PIV-CAC authenticators are common in Okta for
   Government and rare in commercial Okta, which is presumably why this was missed. This
   aborts `list_policies(type="MFA_ENROLL")` for the whole page.

Both are fixed the same way the existing LogSecurityContext.user_behaviors workaround in
system_logs.py handles an analogous SDK gap: relax the overly strict field annotation and
force a Pydantic schema rebuild, rather than waiting on an SDK release. The Policy fix
rebuilds AccessPolicy and any other loaded Policy subclass explicitly (not just the base
class) so it isn't sensitive to which module happens to import the subclass first.

Verified against the live tenant: list_policies(ACCESS_POLICY), list_policies(MFA_ENROLL),
and list_policy_rules on a real rst... access policy all now succeed where they previously
raised on every call.

Fixes okta#100, Fixes okta#101
@jackvaughanjr

Copy link
Copy Markdown
Author

Closing in favour of #107, which supersedes this.

#107 covers the same two defects (#100 and #101) plus two more of the same kind: the five required StrictBool fields on SamlApplicationSettingsSignOn (#48, okta/okta-sdk-python#546) and UserTypeCondition.exclude/.include rejecting null (okta/okta-sdk-python#572).

The patches move out of policies.py into a single okta_mcp_server/utils/okta_compat.py, so all SDK model compatibility lives in one place with per-patch upstream references. The existing LogSecurityContext.user_behaviors workaround in system_logs.py is migrated there too.

The Optional[str] treatment of the authenticator key and the reasoning for it carry over from this PR unchanged. The _embedded fix is extended to cover five more base models declaring the same narrow Dict[str, Dict[str, Any]] shape, and it now handles Pydantic v2 subclass model_fields regardless of import order, with a test running both orders.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

list_policies(type="ACCESS_POLICY") fails: _embedded.resourceType returned as string, Policy model expects dict

1 participant