Commit graph

4669 commits

Author SHA1 Message Date
Sameer Kankute
8e32919074
Merge branch 'main' into litellm_fix_multipart_passthrough 2026-03-11 19:30:45 +05:30
Sameer Kankute
f243e5615f
Merge branch 'main' into litellm_oss_staging_03_10_2026 2026-03-11 18:50:03 +05:30
Sameer Kankute
43217c8a4b
Merge branch 'main' into litellm_oss_staging_03_10_2026 2026-03-11 18:32:17 +05:30
Sameer Kankute
3dab62023c Merge branch 'main' into litellm_oss_staging_03_04_2026 2026-03-11 18:31:20 +05:30
Sameer Kankute
c2fca1124b fix(proxy): preserve multipart/form-data boundary in passthrough endpoints
Fixes issue where multipart file uploads through passthrough endpoints failed with RequestValidationError. The proxy was consuming the request body stream and FastAPI was trying to parse multipart bodies as JSON dicts.

Changes:
- Try JSON parsing first for multipart content-type (handles misconfigured clients)
- Skip multipart parsing if JSON succeeds to avoid stream consumption
- Remove custom_body parameter from endpoint_func to prevent FastAPI auto-parsing
- Check for parsed body before using multipart handler
- Add regression test for multipart boundary preservation

Handles both actual multipart uploads and JSON bodies with incorrect multipart content-type headers.

Made-with: Cursor
2026-03-11 16:52:02 +05:30
yuneng-jiang
ff2f96d09e Fix mcp_tool_permissions JSON string deserialization in _resolve_team_allowed_mcp_servers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 00:23:40 -07:00
yuneng-jiang
860cb17571 Fix cross-team MCP server info disclosure and restricted key bypass
The GET /v1/mcp/server endpoint allowed any authenticated user to pass
an arbitrary team_id and enumerate another team's MCP server config.
Restricted virtual keys could also use the team_id param to bypass
their access limitations. Add team membership check for non-admins
and block restricted keys from using the team_id filter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:48:30 -07:00
yuneng-jiang
c362ae5095 Add team-scoped MCP server filtering for key creation and fix UnboundLocalError
When creating a key, the MCP server list now filters by the selected team's
allowed servers. Also fixes UnboundLocalError on `is_restricted_virtual_key`
when `team_id` query param was provided to GET /v1/mcp/server.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:15:14 -07:00
Cesar Garcia
3d2df7e8b5
Revert "feat: add model_cost aliases expansion support" 2026-03-10 22:39:19 -03:00
Cesar Garcia
6bca746d23
Merge pull request #21601 from Chesars/feat/model-cost-aliases
feat: add model_cost aliases expansion support
2026-03-10 18:07:23 -03:00
Ishaan Jaff
373e5e316b
feat(mcp): BYOM — non-admin MCP server submission + admin review workflow (#23205)
* feat(mcp): add BYOM (Bring Your Own MCPs) submission + admin review workflow

Non-admins can now submit MCP servers for review via POST /v1/mcp/server/register.
Admins get a Submissions tab in the UI to approve or reject pending servers.
Approved servers enter the active runtime; rejected ones stay out with notes.

- DB: add approval_status, submitted_by, submitted_at, reviewed_at, review_notes
  to LiteLLM_MCPServerTable with migration
- Backend: new endpoints register, submissions, approve, reject
- reload_servers_from_database now only loads approval_status=active servers
- UI: Submissions tab with stat cards, card list, confirm dialogs; non-admin
  "Submit MCP Server" button wired to /register endpoint
- Fix get_mcp_submissions to filter by submitted_at IS NOT NULL (not submitted_by,
  which can be null for team-scoped keys without an associated user)

* feat(mcp): rename nav item to Team MCPs + add New badge

* fix(mcp): revert nav label, rename Submissions tab to Team MCPs + New badge

* feat(mcp): add MCP Standards — required fields config + CI-style checks on submissions

Adds a "Standards" tab (admin-only) to MCP Servers where admins define which
server fields are required for a submission to pass. Each submission card in
Team MCPs then shows a green ✓ or red ✗ for each required field, with a
summary "N/M checks" badge in the header — like GitHub CI status rows.

Also adds a `source_url` field (GitHub / Source URL) to the MCP server schema
so non-admins can link to the source repo when submitting a server.

- schema.prisma: add `source_url String?` to LiteLLM_MCPServerTable
- migration: 20260309000001_add_mcp_source_url
- _types.py: source_url on NewMCPServerRequest, UpdateMCPServerRequest, LiteLLM_MCPServerTable
- types.tsx: source_url on MCPServer interface
- create_mcp_server.tsx: GitHub/Source URL form field
- MCPStandardsSettings.tsx: new — toggle which fields are required (stored in general settings as mcp_required_fields)
- mcp_servers.tsx: Standards tab (admin-only)
- MCPSubmissionsTab.tsx: load required fields + CI-style check pills on each card

* refactor(mcp): move submission rules into Team MCPs tab, grouped free-form UI

Folds the Standards tab into Team MCPs. Submission Rules panel now lives at the
top of the Team MCPs tab — collapsible, shows active rules as chips when closed,
expands to a grouped checkbox editor (Documentation / Source / Connection /
Security). Removes the separate Standards tab from the nav.

MCPStandardsSettings.tsx is now constants-only (FIELD_GROUPS, MCP_REQUIRED_FIELD_DEFS,
SETTINGS_KEY) — the UI lives in MCPSubmissionsTab.

* feat(mcp): add mcp_required_fields to ConfigGeneralSettings + config/list endpoint

Registers mcp_required_fields as a proper general_settings field so the UI
can read/write it via /config/list and /config/field/update without the
"Invalid field" error. Also fixes a pre-existing pyright None-check issue
in _sync_ui_settings_to_general_settings.

* ui(mcp): GitHub-style PR checks panel on submission cards

* ui: rename Team MCPs -> Submitted Tools, Team Guardrails -> Submitted Guardrails

* address greptile review feedback (greploop iteration 1)

* fix: inline import, add approval workflow tests, rename Submitted MCPs

* fix(mcp): allow re-approval of rejected MCP server submissions

* fix(mcp): evict rejected servers from runtime; enforce mcp_required_fields on /register

* fix(mcp): sort submissions newest-first; force active status on admin-created servers

* fix(mcp): add missing mock in test, show Approve for rejected, clear submission metadata, drop spurious Content-Type

* fix(mcp/ui): show Reject for active servers; show submit form to non-admins with team-key note

* fix(mcp): conditional reload on reject; view-only admin for submissions; block admin from /register

* fix(mcp): match auth_type required-field validation to UI compliance check (reject 'none')

* fix(mcp): block view-only admin from /register; log settings failure; warn on active server reject

* fix(mcp): allow view-only admin to use /register; add _validate_mcp_required_fields tests

* fix(mcp): validate field names in mcp_required_fields; surface backend error in submit UI

* fix(mcp): fix falsy field check; add field-name validation; add take limit; document server-managed fields; close dialog on error
2026-03-10 13:58:59 -07:00
Jason Roberts
70fca22f68
feat(panw-prisma-airs): PANW Prisma AIRS guardrail with apply_guardrail support (#22999)
* feat(panw-prisma-airs): PANW Prisma AIRS guardrail with apply_guardrail support

* fix(panw): honor masking and fallback behavior

* fix(panw): clean up apply_guardrail MCP metadata handling

* fix(panw): clean up apply_guardrail MCP metadata handling

* fix(panw): harden apply_guardrail edge cases

* fix(panw): apply MCP masked data on allow responses

* fix(panw): scan latest developer message in anthropic mode

* fix(panw): restore legacy user-only pre-call scanning

* fix(panw): record apply_guardrail in applied guardrails header

* fix(panw): scan developer role in legacy pre-call path

* fix(panw): harden SSE parsing and narrow MCP name fallback

* fix(panw): harden streaming attr lookup and document dual scans

* fix(panw): fail closed on permanent 4xx and cover streaming observability
2026-03-10 12:31:31 -07:00
michelligabriele
ffc89e4ef6
fix(mcp): add AWS SigV4 auth for Bedrock AgentCore MCP servers (#22782)
* fix(mcp): add AWS SigV4 auth for Bedrock AgentCore MCP servers

Add aws_sigv4 auth type to MCP client via httpx.Auth subclass that
signs each request with SigV4 using botocore. Enables mcp_servers
config to connect to AgentCore-hosted MCP servers.

* docs(mcp): add AWS SigV4 auth documentation for Bedrock AgentCore

Add dedicated docs page for configuring MCP servers with AWS SigV4
authentication, update MCP overview with aws_sigv4 auth type and
config example, and link from Bedrock AgentCore provider docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mcp): address Greptile review — requires_request_body, full header signing, health check

- Add requires_request_body = True to MCPSigV4Auth so httpx buffers the
  request body before calling auth_flow (prevents empty body hash for
  streaming requests)
- Pass all request headers to AWSRequest for canonical SigV4 signing
  instead of only Content-Type
- Exclude aws_sigv4 from health check skip logic since it has its own
  credential fields (not authentication_token)
- Fix docs: mark aws_access_key_id/aws_secret_access_key as optional
  (falls back to boto3 credential chain)
- Add test for requires_request_body flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Krish Dholakia <krrishdholakia@gmail.com>
2026-03-10 11:11:20 -07:00
Ishaan Jaff
9543d785b5
fix(mcp): don't auto-detect M2M OAuth from field presence (#23187)
* fix(mcp): require explicit opt-in for OAuth2 M2M client_credentials flow

Auto-detecting M2M from client_id+secret+token_url presence broke existing
interactive OAuth setups (e.g. GitHub Enterprise). Add oauth2_flow field and
default has_client_credentials to False — M2M must be explicitly opted into
with oauth2_flow: client_credentials.

* test(mcp): add regression tests for oauth2_flow M2M opt-in behavior
2026-03-10 10:59:49 -07:00
mubashir1osmani
26febf11a7
Merge pull request #23202 from mubashir1osmani/fix/tag-usage-cost-panel-zero
fix: tag usage cost panel zero
2026-03-10 13:57:22 -04:00
mubashir1osmani
88d0f9d834 added comments 2026-03-10 13:56:15 -04:00
Harshit Jain
424a96580b
Merge pull request #22627 from BerriAI/litellm_presidio_antropic_edge_case
fix: presidio edge case with antropic handle on pii token leak
2026-03-10 21:16:56 +05:30
Chesars
a6cb510703 merge: resolve conflicts between main and litellm_oss_staging_03_04_2026
Resolved 14 file conflicts:
- image_edits.md: combined OpenRouter + Black Forest Labs providers
- utils.py: kept staging's message-level cache_control check
- networking.tsx: kept export on 4 tool interfaces
- tool_management_endpoints.py: kept ToolOutputPolicy import
- Accepted main's version for: schema.prisma, a2a_protocol, mcp_server,
  _types.py, auth_checks.py, db_spend_update_writer, endpoints.py,
  spend_tracking_utils, a2a_endpoints, model_prices backup
2026-03-10 10:45:04 -03:00
Harshit28j
2f5a553a7d test: assert setup_database called with correct args
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 18:31:04 +05:30
Harshit28j
aae2deb839 fix: remove redundant import and add test for startup failure
- Remove redundant `import sys` (already imported at module level)
- Add test_startup_fails_when_db_setup_fails verifying sys.exit(1)
  when PrismaManager.setup_database returns False

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 18:15:11 +05:30
Sameer Kankute
9bc7357e7c
Merge pull request #23222 from BerriAI/litellm_oss_staging_02_18_2026
Litellm oss staging 02 18 2026
2026-03-10 17:46:06 +05:30
Sameer Kankute
9bee76c30d
Merge pull request #23253 from BerriAI/main
merge main
2026-03-10 17:25:01 +05:30
Sameer Kankute
3f17a63b81
Merge branch 'main' into litellm_oss_staging_03_02_2026 2026-03-10 17:19:37 +05:30
Harshit28j
631aefea18 fix: req changes on feedback from greptile 2026-03-10 16:46:18 +05:30
Harshit28j
212059cd11 fix: presidio improvements 2026-03-10 16:33:41 +05:30
Harshit28j
12de8a724f fix: clean approach instead of UUID 2026-03-10 15:51:11 +05:30
Harshit28j
14ecc79760 fix: req changes greptile hallucinates 2026-03-10 15:51:11 +05:30
Harshit28j
1bfd88a33c fix: req changes 2026-03-10 15:51:10 +05:30
Harshit28j
861db111a0 fix: presidio edge case with antropic handle on pii token leak 2026-03-10 15:51:10 +05:30
Sameer Kankute
f33f446476
Merge pull request #23240 from BerriAI/litellm_bedrock-nova-output-config-spend-tracking
fix(bedrock): strip output_config from Converse requests; fix spend tracking redaction test
2026-03-10 12:15:06 +05:30
yuneng-jiang
b8886568f0
Merge pull request #23236 from BerriAI/litellm_/flamboyant-leakey
[Fix] All Proxy Models Not Including Model Access Groups in Key Creation
2026-03-09 23:27:38 -07:00
Sameer Kankute
200b001633 fix(bedrock): strip output_config from Converse requests; fix spend tracking redaction test
Made-with: Cursor
2026-03-10 11:53:36 +05:30
Sameer Kankute
db99fdeff3 fix(mcp): OpenAPI tool listing and execution for relative URLs and camelCase
- Fix case-insensitive tool name matching in _tool_name_matches() so that
  OpenAPI operationIds (camelCase) match lowercase registered tool names
  when filtering by allowed_tools
- Fix get_base_url() to resolve relative server URLs (e.g. /api/v3) by
  deriving full base URL from spec_path when OpenAPI spec has relative URLs
- Add tests for case-insensitive matching and filter_tools_by_allowed_tools

Made-with: Cursor
2026-03-10 11:34:23 +05:30
yuneng-jiang
1755a281bd Fix mutation bug: copy lists in get_key_models to prevent corrupting cached UserAPIKeyAuth
`all_models = user_api_key_dict.models` was creating an alias, so
`_get_models_from_access_groups` (which uses `.pop()`/`.extend()`) would
mutate the cached object in-place. Now both `.models` and `.team_models`
assignments create copies via `list()`.

Added test to verify the input is not mutated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:55:53 -07:00
yuneng-jiang
1cf191d9ad [Fix] Deduplicate model lists and remove dead assignment
Adds dedup to get_key_models and get_team_models to prevent duplicate
entries when access group member models overlap with proxy_model_list.
Removes dead assignment of all_models in get_team_models.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:44:45 -07:00
yuneng-jiang
c829733200 [Fix] Include model access groups when expanding All Proxy Models
When a team has "all-proxy-models", the model list expansion now includes
model access group names so they appear in the UI key creation form.
Also fixes get_key_models not forwarding include_model_access_groups to
_get_models_from_access_groups, and removes unused _unfurl_all_proxy_models.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:30:07 -07:00
Krish Dholakia
992510154c
Merge branch 'main' into litellm_oss_staging_02_18_2026 2026-03-09 19:48:46 -07:00
mubashir1osmani
7ffaa6f74e fix(tag-usage): remove broken compute_tag_metadata_totals causing cost panel to show 0
The tag daily activity endpoint used compute_tag_metadata_totals which
deduplicates by request_id, but LiteLLM_DailyTagSpend stores aggregated
daily records where request_id is either NULL or stale. This caused all
metadata totals (total_spend, total_requests, etc.) to be 0 in the UI
cost panel. Now uses the same standard totals as every other entity type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:48:27 -04:00
yuneng-jiang
8ecac84789
Revert "feat(proxy): add Prisma DB pool and engine health metrics to Promethe…"
This reverts commit 0bb26c3f1b.
2026-03-09 14:55:11 -07:00
yuneng-jiang
c1b3640310 feat: add audit log export to external callbacks (S3, Datadog, etc.)
Audit logs (CRUD events on keys, teams, users, models) were only stored in
the Prisma DB. This adds a pluggable callback system so audit logs can be
forwarded to external services like S3 for ingestion into security monitoring
tools.

New config key `audit_log_callbacks` under `litellm_settings` reuses the
existing callback infrastructure. Any CustomLogger subclass can opt in by
overriding `async_log_audit_log_event()`. S3Logger (s3_v2) is implemented
as the first handler, storing audit logs under `audit_logs/{date}/` prefix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:48:10 -07:00
Aarish Alam
e21b06265a
fix fkey violation on deleting user (#23115) 2026-03-09 08:53:11 -07:00
ohadgur
0bb26c3f1b
feat(proxy): add Prisma DB pool and engine health metrics to Prometheus (#22655)
* feat(proxy): add Prisma DB pool and engine health metrics to Prometheus

Add a PrismaMetricsCollector that periodically queries pg_stat_activity
and the Prisma engine process to expose connection pool and engine health
as Prometheus gauges/counters. Auto-enabled when prometheus_system is in
service_callback.

New metrics:
- litellm_db_pool_active_connections (Gauge)
- litellm_db_pool_idle_connections (Gauge)
- litellm_db_pool_total_connections (Gauge)
- litellm_db_pool_waiting_connections (Gauge)
- litellm_db_engine_up (Gauge)
- litellm_db_engine_restarts_total (Counter)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Greptile review feedback

- Only increment engine_restarts counter on heavy reconnects (engine
  actually dead), not lightweight network-blip reconnects
- Fix potential KeyError in _get_or_create_gauge/counter fallback path
  when REGISTRY._names_to_collectors is absent
- Rename litellm_db_pool_waiting_connections to
  litellm_db_pool_lock_waiting_connections to clarify it measures lock
  contention, not pool slot queuing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: warn when prometheus_system enabled but watchdog disabled

Log a warning when users have prometheus_system in service_callback
but PRISMA_HEALTH_WATCHDOG_ENABLED=false, since DB pool and engine
metrics won't be collected in that configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: retrigger CI checks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: use labeled gauge for DB pool connection metrics

Replace 3 separate pool gauges (active, idle, total) with a single
`litellm_db_pool_connections` gauge using a `state` label. This is more
Prometheus-idiomatic and exposes all pg_stat_activity states (active,
idle, idle in transaction, etc.) without ambiguity about what "total"
includes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Greptile review — stale labels and fallback re-registration

- Zero out known pg_stat_activity states that are absent from the current
  query result, preventing stale gauge values from persisting.
- Simplify _get_or_create_gauge/counter by removing the fallback loop
  that could re-register an already-registered metric (ValueError).
- Add test for stale label clearing across collection cycles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: include "unknown" in _PG_STATES for stale label clearing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: collect immediately on start and consolidate into single query

- Move sleep to end of loop so metrics appear on /metrics immediately
  after startup instead of after a 30s delay.
- Combine pool state and lock waiting queries into a single SQL query
  using conditional aggregation, halving per-cycle DB overhead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prevent tight spin loop on collection error

Move asyncio.sleep outside the try/except so it always executes even
when _collect_engine_health() or _collect_pool_metrics() raises.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add multiprocess_mode to _get_or_create_gauge initialization

- Include `multiprocess_mode` parameter to properly support multiprocessing in Gauge creation.
- Ensure consistent behavior for labeled and unlabeled Gauges.

* fix: handle invalid env var and document watchdog prerequisite

- Add try/except ValueError for PRISMA_METRICS_COLLECTION_INTERVAL_SECONDS
  to prevent proxy startup crash on non-numeric values (e.g. "30s")
- Document that DB metrics require both prometheus_system callback and
  PRISMA_HEALTH_WATCHDOG_ENABLED=true

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use defensive null coalescing for query_raw row values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add invalid env var fallback test and fix mock signature

- Add test for non-numeric PRISMA_METRICS_COLLECTION_INTERVAL_SECONDS
- Add **kwargs to mock _patched_get_or_create_gauge for forward compat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 08:49:46 -07:00
milan-berri
df2e1bca46
feat: allow JWT and OAuth2 auth to coexist on the same instance (#23153)
When both enable_jwt_auth and enable_oauth2_auth are True, the proxy now
routes tokens based on their format:
- JWT tokens (3 dot-separated parts) -> JWT auth handler
- Opaque tokens -> OAuth2 auth handler

This enables using JWT for human users and OAuth2 for M2M (machine) clients
on the same LiteLLM instance. Previously, enabling OAuth2 would intercept
all tokens on LLM API routes before JWT auth could run.

When only one auth method is enabled, behavior is unchanged (backward compatible).
2026-03-09 08:41:27 -07:00
Joe Reyna
36e04b6efe
fix(tests): restore litellm_params=None on mock agent in a2a invoke test (#23125) 2026-03-09 07:16:02 -07:00
Joe Reyna
0bc1bd6871
fix(tests): use AsyncMock for prisma find_unique in agent get-by-id test (#23122) 2026-03-09 07:13:50 -07:00
yuneng-jiang
3a1ac964f7 fix: pass organization_ids=None in get_users test calls
When calling get_users() directly (not via FastAPI), Query() defaults
are not resolved. Pass organization_ids=None explicitly to avoid
'Query' object has no attribute 'split' error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-07 23:15:16 -08:00
yuneng-jiang
ac5128493e fix: repair test regressions from org admin auth changes
- test_get_users_*: pass proxy admin user_api_key_dict since get_users
  now calls _authorize_user_list_request which checks user_role
- test_validate_team_member_add_permissions_non_admin: set
  organization_id on mock team since _is_user_org_admin_for_team
  accesses it

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-07 23:10:53 -08:00
yuneng-jiang
8f33983389 Merge remote-tracking branch 'origin' into litellm_org_admin_add_user_e2e 2026-03-07 22:58:57 -08:00
yuneng-jiang
ce317148b9 feat: org admin access to team management — backend auth, UI visibility, tests
- Add _is_user_org_admin_for_team() reusable helper to common_utils.py
- Grant org admins access to /team/list, /team/info, /team/member_add,
  /team/member_delete, /team/member_update, /team/model/add,
  /team/model/delete, /team/permissions_list, /team/permissions_update
- Make validate_membership async with org admin fallback
- Add /user/list to self_managed_routes (endpoint handles own auth)
- UI: org admins see Members, Member Permissions, Settings tabs in team view
- UI: CreateUserButton uses useOrganizations() for org dropdown
- UI: org admin delete-member respects disable_team_admin_delete_team_user
- Add 16 unit tests for _is_user_org_admin_for_team, validate_membership,
  _user_is_org_admin route check, and privilege escalation prevention

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-07 22:43:09 -08:00
yuneng-jiang
8bf3c0c67f fix: org admin invite user — multi-org selector, organizations list in POST body, auth check
- Thread org objects {organization_id, organization_alias} instead of bare IDs from
  users/page.tsx → view_users.tsx → CreateUserButton so the selector can show aliases
- Replace single-select org dropdown with multi-select; always shown when organizationIds
  is non-null; disabled/pre-selected for single-org admins; displays "Alias (id)"
- handleCreate: maps organization_ids → organizations before POST, removes redundant
  organizationMemberAddCall (backend _add_user_to_organizations handles it)
- _user_is_org_admin: also checks organizations list field in addition to singular
  organization_id so /user/new succeeds for org admins
- Add 5 backend unit tests for _user_is_org_admin and 2 frontend tests for new form behavior

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 20:34:12 -08:00