Let users sign in with their directory (Active Directory / OpenLDAP)
credentials through the existing login form. Reuses sso_providers
(kind='ldap') and user_sso_identities, so no schema change is needed.
Authentication is search-then-bind: a service account resolves the user and
only the returned DN is bound with the submitted password, so user input is
never used as a bind identity. Roles come from a role template at first
provisioning only; changing a directory group later never re-templates an
existing account. Directory outages and wrong passwords stay distinguishable
(502 LDAP_UNAVAILABLE vs 401 AUTH_FAILED).
Hardening from review:
- An account holding its own password stops at the local check, so a local
secret is never forwarded to the directory nor counted against that
directory's own lockout policy.
- A lookup without a unique exact identifier match is refused instead of
being bound against the first hit.
- A failure during the user bind surfaces as LDAP_UNAVAILABLE rather than a
401 credential verdict.
- Binds are throttled before they reach the directory, keyed per identifier
and client address, with the address resolved from a trusted peer so a
spoofed X-Forwarded-For cannot reset the budget.
- Enabling a plain ldap:// URL without StartTLS is rejected; a disabled
certificate check is surfaced as a warning.
- auto_provision defaults to off, with an optional allowed_groups allow-list
and a group-search mode (member / uniqueMember / memberUid) for directories
that do not expose memberOf. Nested groups are not resolved.
- The identity key is configurable (entryUUID / objectGUID) and blank by
default; a connectivity probe reports which attribute the directory
exposes.
- Config changes are audited against the acting admin, and referrals are no
longer followed during binds.
Tests cover the above without a live directory (ldap3.Connection is the only
thing replaced); a live test exercises a real directory when configured.
Co-authored-by: jubaoliang <jubaoliang@gmail.com>
Apply the remaining working-tree changes: connectors empty-state layout,
plus the knowledge, HITL, captcha, workspace, user-cache, and SSRF
adjustments with matching tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds 极验行为验 v4 to the login captcha layer, following the provider
plug-in seam (one dataclass + register call backend-side, one widget
adapter frontend-side; the settings UI is data-driven so only locale
keys were needed):
- providers.py: _GeetestV4Provider — login token carries the frontend
getValidate() result as JSON (lot_number / captcha_output /
pass_token / gen_time); siteverify posts form-urlencoded to
gcaptcha4.geetest.com/validate?captcha_id=<id> with
sign_token = HMAC-SHA256(captcha_key, lot_number); only
result == "success" passes (fail / status:error both reject).
Aliases: geetest, geetest4, gt4. Fail-closed on transport errors,
consistent with the other providers.
- CaptchaField: geetest-v4 popup via gt4.js bind mode — initGeetest4,
showCaptcha on ready, getValidate() serialized as the captcha_token;
error/close resolve undefined so login re-prompts.
- Settings UI: captcha_id / captcha_key fields via locale keys.
Tests: provider unit tests (URL + form fields + HMAC against an
independent computation, malformed/missing-field rejection, interpret
matrix), ensure_captcha flow through a local siteverify double (form
body + signature asserted; fail result rejects), popup adapter tests
(validate payload JSON, error path), builtin-order + integration list
assertions updated. Locale parity green.
* feat: add in-tree plugin marketplace and related dashboard polish
Ship a catalog install flow (market API + Admin UI), stop auto-seeding new
bundled plugins, and include storage rename, plugin tools listing, agent
lifecycle locking, and history/trajectory path cleanup in the same change.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(dashboard): use ToolsTabs in Experts tools drawer
Align the Experts tools catalog with Personalization so builtin, plugin,
and ACP tabs are available in the same drawer.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: per-tool chat icons and init seed expectation
Show distinct Lucide icons for common built-in tools in chat tool rows,
and stop expecting octop init to auto-copy catalog plugins after the
marketplace seed change.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(test): drain captcha siteverify POST bodies on Windows
Unread form bodies from the mock HTTP server could leave the connection
half-closed and flake reCAPTCHA v3 multi-call assertions under Windows CI.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(auth): add optional login captcha (slider, turnstile/hcaptcha/recaptcha, tencent)
Password login can now be guarded by a captcha. Default slider stays
client-only; strong providers are verified server-side with the vendor
(Cloudflare Turnstile, hCaptcha, reCAPTCHA v2/v3, Tencent Cloud Captcha).
- infra/auth/captcha: env snapshot + settings blob resolution, provider
registry with per-vendor verify calls, siteverify with 10s timeout
- GET /api/auth/captcha (public widget config); POST /api/auth/login
accepts captcha_token; tencent token carries ticket:randstr and is
checked via GET with the client IP
- admin GET/PUT /api/settings/captcha behind the new captcha permission;
OCTOP_CAPTCHA_SECRET redacted in envs API; boot validation for strong
env providers
- dashboard: CaptchaField with slider/checkbox/invisible/popup modes,
login page wiring, advanced-settings captcha panel
- docs + i18n (en/zh) + unit/integration/frontend tests
* fix(captcha): verify Tencent tickets via DescribeCaptchaResult (TC3)
The legacy ssl.captcha.qq.com/ticket/verify endpoint rejects current
tickets with response=15 "decrypt fail" even for correct
CaptchaAppId/AppSecretKey pairs. Switch to the official ticket-check API:
- POST captcha.tencentcloudapi.com DescribeCaptchaResult (2019-07-22),
TC3-HMAC-SHA256 signed with CAM API keys; AppSecretKey stays in the body
- captcha settings gain cam_secret_id/cam_secret (encrypted at rest,
env fallback OCTOP_CAPTCHA_CAM_SECRET_ID/KEY); settings view exposes
cam_secret_id + has_cam_secret; dashboard shows the two fields for the
tencent provider only
- interpret maps CaptchaCode (1 OK; 7/8/9/15/16/21/100 fail) and
EvilLevel=100 (malicious) instead of the legacy response=="1"
- TC3 signer moved voice/tencent_sign.py -> utils/tencent_sign.py (shared)
- drop the redundant Host header from signed requests: httpx sets Host
from the URL (same value the signature covers), and an explicit Host
let host-routing system proxies intercept localhost mock calls
- secrets no longer appear in httpx URL logs (payload is in the body)
- move the Login Captcha settings tab next to HTTPS
* polish(dashboard): show reCAPTCHA v3 min score only when that provider is active
* fix(dashboard): captcha widgets follow the site locale, not the browser
- tencent popup: userLanguage from i18n.language (was navigator.language)
- turnstile: pass language render option
- hcaptcha/recaptcha/recaptcha-v3: hl script param from UI locale
- slider already renders site-provided labels
* feat(captcha): unlist reCAPTCHA v2 from the settings catalog
No verified deployment key for v2; keep the provider registered so
existing configs still verify, but stop offering it in the dropdown
(list_providers now filters on a per-provider listed flag).
* feat(cli): octop captcha reset — offline lockout escape hatch
Clears the stored captcha settings blob so login falls back to the
built-in slider when a misconfigured provider blocks all logins
(settings UI requires login, so it cannot fix itself). Boot-env
captcha is untouched; a running server needs a restart.
CHANGELOG: cover reset command, TC3 switch, widget language, v2 unlist.
* feat(cli): register octop captcha reset command and add changelog
Without the registry entry the command module from c5a7649d was
unreachable; CHANGELOG covers the login captcha feature, the TC3
ticket-check switch, widget locale, v2 unlisting, and captcha reset.
* fix(captcha): close pre-merge gaps + document the provider architecture
- envs API: redact OCTOP_CAPTCHA_CAM_SECRET_KEY like OCTOP_CAPTCHA_SECRET
(was exposed in plaintext by GET /api/envs)
- validate_boot: env-only tencent provider now requires the CAM keys,
failing fast at boot instead of at first login
- docs/configuration.md: document the two CAM env vars; note recaptcha
v2 is unlisted but resolvable
- providers.py: architecture docstring (four layers + settled design
decisions + adding-a-vendor recipe); rename _SuccessProvider to
_FormPostProvider; drop the register() cast, which exposed a real
structural bug: frozen-dataclass providers never satisfied the
Protocol's mutable metadata members — declare them read-only
properties so the structural check is real
- verify.py: docstring naming it the execution layer
* fix(auth): update issuer handling in SSO service and enhance ID token verification tests
- Refactor the issuer parameter in the SsoService to use a dynamic endpoint method instead of stripping trailing slashes.
- Add tests to verify ID token acceptance with trailing slashes and rejection for mismatched issuers.
- Enhance callback tests to ensure ID token verification aligns with the discovery issuer.
* fix(tests): streamline SSO service login test for clarity
- Consolidate the parameters in the `start_login` method call within the ID token verification test to enhance readability and maintainability.