Skip to content

fix(team_endpoints): auto-add SSO team members to org on move (proxy admin only)#26377

Open
ishaan-berri wants to merge 5 commits intolitellm_internal_stagingfrom
litellm_fix_team_org_sso_members
Open

fix(team_endpoints): auto-add SSO team members to org on move (proxy admin only)#26377
ishaan-berri wants to merge 5 commits intolitellm_internal_stagingfrom
litellm_fix_team_org_sso_members

Conversation

@ishaan-berri
Copy link
Copy Markdown
Contributor

@ishaan-berri ishaan-berri commented Apr 24, 2026

Relevant issues

Fixes SSO/Entra setups where proxy admins get a 403 when moving teams to organizations because the org membership table is always empty (no SCIM sync).

Fixes LIT-2447

Pre-Submission checklist

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Type

🐛 Bug Fix

Changes

Problem: When a proxy admin calls PATCH /team/update to assign organization_id, validate_team_org_change checks that every team member is already an org member and returns 403 if not. In SSO/Entra setups without SCIM, users are auto-created as team members on login but the org membership table stays empty — so this always 403s.

Fix:

  • validate_team_org_change gets an is_proxy_admin flag. Proxy admins bypass the membership check; team admins keep the original strict check (prevents privilege escalation — a team admin can't inject users into an org they don't control).
  • New _auto_add_team_members_to_organization helper: when a proxy admin moves a team, any team members not already in the org are silently added as internal_user. Errors (duplicate key, race condition) are logged at DEBUG and skipped.
  • fetch_and_validate_organization threads user_api_key_dict through so it can derive is_proxy_admin and call the helper.

Security model:

  • Proxy admins: membership check skipped; missing members auto-added to org as internal_user
  • Team admins: original check preserved — all members must already be in the target org

Tests: tests/test_litellm/proxy/test_team_org_move.py — 9 unit tests covering proxy-admin bypass, team-admin block/pass, same-org short-circuit, default-user exclusion, and error resilience.

Screenshots / Proof of Fix

Before — proxy admin gets 403 moving a team with SSO users to an org:

before_403_error

After — team moves successfully, org field populated:

after_org_assigned

@ishaan-berri
Copy link
Copy Markdown
Contributor Author

@greptileai

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 24, 2026

Greptile Summary

This PR fixes a 403 error affecting proxy admins in SSO/Entra setups where org membership tables are empty due to no SCIM sync. It adds an is_proxy_admin bypass to validate_team_org_change and a new _auto_add_team_members_to_organization helper that silently upserts missing team members into the target org as internal_user when a proxy admin moves a team. Team admins retain the original strict membership check, preserving the existing security model.

Confidence Score: 5/5

Safe to merge — the fix is well-scoped, the security model is preserved, and all remaining findings are minor style suggestions.

No P0 or P1 findings. The proxy-admin bypass is correctly gated behind an existing auth check in update_team. The _auto_add_team_members_to_organization helper is only triggered for proxy admins and auto-adds members only as internal_user. The 9 new unit tests cover all key scenarios. The only issues are style-level: a loop-invariant guard placed inside the loop, a duplicate model construction, and manual monkey-patching in tests.

No files require special attention.

Important Files Changed

Filename Overview
litellm/proxy/management_endpoints/team_endpoints.py Adds _auto_add_team_members_to_organization helper and threads is_proxy_admin through validate_team_org_change and fetch_and_validate_organization; logic is sound, minor style issues (redundant organization_id guard inside loop, duplicate LiteLLM_TeamTable construction).
tests/test_litellm/proxy/test_team_org_move.py New test file covering all key scenarios (proxy-admin bypass, team-admin block/pass, same-org short-circuit, default-user exclusion, error resilience); tests use manual monkey-patching instead of unittest.mock.patch, which is functional but less idiomatic.

Sequence Diagram

sequenceDiagram
    participant Client
    participant update_team
    participant fetch_and_validate_organization
    participant validate_team_org_change
    participant _auto_add_team_members_to_organization
    participant add_member_to_organization
    participant DB

    Client->>update_team: PATCH /team/update (organization_id)
    update_team->>update_team: check caller is PROXY_ADMIN or dest-org admin
    update_team->>fetch_and_validate_organization: organization_id, existing_team_row, user_api_key_dict
    fetch_and_validate_organization->>DB: find org + members
    fetch_and_validate_organization->>fetch_and_validate_organization: derive is_proxy_admin
    fetch_and_validate_organization->>validate_team_org_change: team, org, is_proxy_admin
    alt is_proxy_admin = true
        validate_team_org_change-->>fetch_and_validate_organization: skip membership check
        fetch_and_validate_organization->>_auto_add_team_members_to_organization: team, org, prisma_client
        loop each team member not in org
            _auto_add_team_members_to_organization->>add_member_to_organization: user_id, INTERNAL_USER
            add_member_to_organization->>DB: upsert org membership
            DB-->>add_member_to_organization: ok or dup-key caught+debug-logged
        end
    else is_proxy_admin = false
        validate_team_org_change->>validate_team_org_change: all members must be in org
        alt missing members
            validate_team_org_change-->>Client: 403 HTTPException
        end
    end
    fetch_and_validate_organization-->>update_team: org_row
    update_team->>DB: update team.organization_id
    update_team-->>Client: 200 updated team
Loading

Reviews (2): Last reviewed commit: "style: black formatting for team_endpoin..." | Re-trigger Greptile

Comment on lines +1295 to +1300
except Exception as e:
verbose_proxy_logger.debug(
"_auto_add_team_members_to_organization: skipping user_id=%s - %s",
member.user_id,
e,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Overly broad exception silencing

All exceptions from add_member_to_organization are re-raised as ValueError there, and every one of them is caught here and logged only at DEBUG. This means real failures (DB connection loss, unexpected constraint violations, network timeouts) are completely invisible in production logs — the team move silently succeeds while some members are never actually added to the org, leaving a data inconsistency with no operator-visible signal.

Consider logging unexpected exceptions at WARNING or ERROR and reserving DEBUG only for known-benign errors like unique-constraint violations (e.g. check "duplicate" in str(e).lower() before deciding the log level).

@veria-ai
Copy link
Copy Markdown

veria-ai Bot commented Apr 24, 2026

Low: Authorization check relaxation for proxy admins only

This PR allows proxy admins to bypass the team-member org-membership check when moving teams between organizations, auto-adding missing members as INTERNAL_USER. The bypass is properly gated behind a proxy admin role check derived from the authenticated user_api_key_dict. The default behavior for non-admin users is unchanged and still enforces that all team members must already be org members before a team move. No security issues found.


Status: 0 open
Risk: 2/10

Posted by Veria AI · 2026-04-24T01:44:17.701Z

@ishaan-berri ishaan-berri temporarily deployed to integration-postgres April 24, 2026 01:42 — with GitHub Actions Inactive
@ishaan-berri ishaan-berri temporarily deployed to integration-postgres April 24, 2026 01:42 — with GitHub Actions Inactive
@ishaan-berri ishaan-berri temporarily deployed to integration-postgres April 24, 2026 01:42 — with GitHub Actions Inactive
@ishaan-berri ishaan-berri temporarily deployed to integration-postgres April 24, 2026 01:42 — with GitHub Actions Inactive
@ishaan-berri ishaan-berri temporarily deployed to integration-postgres April 24, 2026 01:42 — with GitHub Actions Inactive
Copy link
Copy Markdown
Collaborator

@ryan-crabbe-berri ryan-crabbe-berri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

3 participants