diff --git a/CHANGELOG.md b/CHANGELOG.md index a03ba795..81518a88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ ### Fixed +- **OIDC allowlist values containing spaces (e.g. a group named `Hermes Users`) are no longer split into separate entries.** The allowlist parser split comma-separated values on all whitespace, so a multi-word group/user name became two spurious entries. Allowlist parsing now splits only on commas/newlines (multi-word values stay intact and match their exact signed claim — strictly tightening, no security loosening), while OAuth **scope** parsing keeps its space-delimited behavior per RFC 6749 §3.3. A blank allowlist entry no longer bricks an OIDC-only deployment. Thanks @webtecnica. (#6244) + - **`/sessions` and `/resume` typed in the composer now open the session browser instead of being sent as chat text.** Both commands appeared in the slash-command autocomplete (they're exposed by `/api/commands`) but the WebUI send-time dispatch didn't handle them, so they were posted to the agent as plain text. They now open the native WebUI session browser and clear the composer — on desktop and (via the mobile-aware opener) on phone-width layouts. Thanks @webtecnica. (#6245, #6224) - **Z.AI GLM models now advertise the correct reasoning controls per version.** The WebUI offered the full `reasoning_effort` ladder (max/xhigh/high/medium/low/minimal) for every GLM model, but per Z.AI's API only GLM-5.2+ supports the effort ladder — GLM-4.5 through 5.1 support the binary `thinking` on/off toggle, and GLM-4.7 forces thinking on (not configurable). Reasoning controls are now gated correctly: GLM-5.2+ keeps the full ladder + None, GLM-4.5/4.6/5.0/5.1 get a two-way On/None thinking toggle (no effort levels), and GLM-4.7 shows forced thinking with no toggle. All three surfaces (config status, effort resolution, composer chip) agree across model tiers, provider aliases, and namespaces. Thanks @rh-id. (#6219) diff --git a/api/auth_oidc.py b/api/auth_oidc.py index 9bd9b0f2..384bae2b 100644 --- a/api/auth_oidc.py +++ b/api/auth_oidc.py @@ -207,7 +207,22 @@ def _normalize_scopes(raw: Any) -> list[str]: def _normalize_allow_values(raw: Any) -> list[str]: - return _normalize_text_list(raw) + """Normalize allowlist values, splitting only on commas/newlines. + + Unlike ``_normalize_text_list`` (which also splits on whitespace), this + preserves multi-word values such as OIDC group names containing spaces + (e.g. ``"Hermes Users"`` stays as one entry). + + RFC 6749 §3.3 requires space-delimited scope strings, so + ``_normalize_scopes`` must keep using ``_normalize_text_list`` -- this + function is for allow-values only. + """ + if raw is None: + return [] + if isinstance(raw, (list, tuple, set)): + return [value for value in (str(item).strip() for item in raw) if value] + text = str(raw).replace("\n", ",") + return [part.strip() for part in text.split(",") if part.strip()] def _normalize_text_list(raw: Any) -> list[str]: diff --git a/tests/test_issue3825_oidc_auth.py b/tests/test_issue3825_oidc_auth.py index 199a5aa5..4307e732 100644 --- a/tests/test_issue3825_oidc_auth.py +++ b/tests/test_issue3825_oidc_auth.py @@ -547,3 +547,23 @@ def test_coerce_numeric_claim_rejects_non_finite_values(value): with pytest.raises(OIDCAuthError, match="claim exp was not numeric"): auth_oidc._coerce_numeric_claim({"exp": value}, "exp") + + +def test_normalize_allow_values_and_scopes_use_separate_delimiters(): + """#6244: allowlist values are comma/newline-delimited (multi-word group names + like "Hermes Users" stay intact), while OAuth scopes stay space-delimited + (RFC 6749 §3.3). The two parsers must NOT share whitespace-splitting.""" + from api import auth_oidc + + # Allowlist: multi-word group name stays ONE entry; commas/newlines split. + assert auth_oidc._normalize_allow_values("Hermes Users") == ["Hermes Users"] + assert auth_oidc._normalize_allow_values("Hermes Users, Admins") == ["Hermes Users", "Admins"] + assert auth_oidc._normalize_allow_values("a\nb") == ["a", "b"] + # Blank collection elements are filtered (a bare [""] must not brick OIDC login). + assert auth_oidc._normalize_allow_values([""]) == [] + assert auth_oidc._normalize_allow_values(["Hermes Users", "", " "]) == ["Hermes Users"] + + # Scopes: space-delimited per RFC 6749 §3.3 — whitespace splitting is retained. + assert auth_oidc._normalize_text_list("openid profile email") == ["openid", "profile", "email"] + scopes = auth_oidc._normalize_scopes("openid profile email") + assert scopes[:3] == ["openid", "profile", "email"]