Skip to content

v0.0.8

Choose a tag to compare

@github-actions github-actions released this 01 Sep 02:45
· 1221 commits to master since this release

v0.0.8 — 安全加固与登录页改版

修复 3 个可导致提权 / 凭据泄露的安全问题(指定渠道越权、用户列表暴露 access_token),并全面改版登录注册页:新增《服务条款》《隐私政策》页面、注册需同意条款、补充无障碍支持;同时把 README 国际化扩展至 8 种语言,并加入"启动时校验内嵌主题"的防御性检查,避免升级后因数据库残留旧主题字段导致后台空白页。

中文

🔒 安全

  • 修复指定渠道越权漏洞(one-api#2410):此前任意已登录用户均可通过 /v1/oneapi/proxy/:channelid/*target 指定任意上游渠道,绕过分组隔离与模型白名单并触发上游凭据转发。现在该 URL 参数路径与 token 后缀路径一样,仅管理员可用。
  • 指定渠道时重新校验分组 / 模型白名单(one-api#2410):即使管理员使用指定渠道功能,转发前也会再次确认该渠道确实服务于当前用户分组与所请求模型,作为纵深防御。
  • 修复用户列表接口泄露 access_token(one-api#2425):管理员批量用户接口(GET /api/user/、GET /api/user/search)不再返回用户的 access_token,避免被重放后提权为 root(现有单用户读取路径的保护保持一致)。
  • 为上述修复补充模型层辅助方法 Channel.ContainsGroup / Channel.ContainsModel 及相应回归测试。

✨ 新增功能

  • 登录 / 注册 / 找回密码 / 重置密码四个鉴权页全面改版:抽出新的 AuthLayout(带装饰性渐变光晕背景与品牌位 Logo),统一语义化标题(h1 + 副标题)与表单无障碍 aria-label;forgot-link / form-alert 等局部样式整理后视觉与交互更一致。
  • 新增 《服务条款》页面(/terms) 与 《隐私政策》页面(/privacy),使用新的 LegalLayout(顶部导航 + 法律文件版式)排版;路由免登录即可访问,并已在 router/index.js 的白名单中放行,避免跳转登录页。
  • 注册流程新增「我同意《服务条款》和《隐私政策》」勾选(带 aria-label),未勾选不可提交注册,并在条款文案上提供 /terms、/privacy 直达链接。
  • 套餐页新增空状态:未配置任何套餐时展示友好的插画 + 提示文案("暂无可用套餐 / 请联系管理员配置套餐后再来查看"),替代空白网格。
  • 新增 Channel.ContainsGroup / Channel.ContainsModel 白名单辅助方法(按 , 拆分、精确匹配,空配置视为全部放行),为下游的鉴权与重校验逻辑提供复用基础。
  • 启动时校验前端主题:新增 common.ValidateEmbeddedTheme(buildFS, themesRoot, theme) 与 common.ListEmbeddedThemes(buildFS, themesRoot) 辅助函数;main.go 在 using theme <name> 日志之后调用校验器,如果当前主题未被打入二进制,会输出 [ERROR] 日志并指出 web/build/<theme>/index.html 缺失、列出实际已内嵌的全部主题,并给出三种修复建议(修改 options.theme / 调整 THEME 环境变量 / 重新打包时把期望主题加入 web/THEMES)。该检查非致命,服务器仍会启动以便运维修复,但能立刻把"后台空白"问题暴露在日志里。

🐛 问题修复

  • 修复订单页多了一层 page-container 内边距导致与其它页面边距不一致的问题。
  • 修复普通用户可绕过分组隔离 / 模型白名单指定上游渠道的安全问题(详见「安全」)。
  • 修复管理员用户列表 / 搜索接口可能暴露 access_token 的安全问题(详见「安全」)。
  • router/web.go 不再静默吞掉 web/build/<theme>/index.html 读取失败的错误,改为通过 logger.SysError 打印明确错误("theme %q is not embedded ...")作为兜底,确保即使绕过启动检查也能在路由层看到失败原因。

📚 文档

  • README 国际化扩展至 8 种语言:新增德语、阿拉伯语、韩语、俄语、日语、繁体中文 README,主 README 与英文 README 的语言导航同步更新。

🔧 工程 / CI

  • 补充三组回归测试:中间件指定渠道鉴权(middleware/auth_test.go)、用户列表 access_token 脱敏(controller/user_test.go)、模型白名单辅助方法(model/channel_contains_test.go)。
  • 为内嵌主题校验辅助函数补充 5 个单元测试(common/embed_theme_test.go,含 embed_theme_testdata/ 测试资源),覆盖:列出主题、主题存在、主题缺失、空主题、根路径带尾斜杠容错。

⚠️ 升级注意事项

  • 本次为代码与前端改动,无数据库迁移,可直接升级。
  • 登录 / 注册 / 找回密码页面已改版,建议升级后清除浏览器缓存或以无痕模式验证。
  • 若你在开放注册场景下依赖「匿名指定渠道代理」能力,请注意该能力现已被移除:仅管理员可通过 URL 参数或 token 后缀指定渠道。
  • 数据库残留的旧 options.theme 会被新校验器捕获:如果你从较早版本(例如内置 default 主题的旧 one-api / one-api-pro)升级,启动日志中若出现 theme "default" is not embedded in this binary ... 的 [ERROR],说明 MySQL options 表里仍保留着旧的 theme=default 记录。请执行 UPDATE options SET value='default-pro' WHERE \key`='theme';`(或直接删除该行)后重启即可。

English

🔒 Security

  • Fixed privilege escalation via URL-parameter channel pinning (one-api#2410): any authenticated user could previously call /v1/oneapi/proxy/:channelid/*target to pin an arbitrary upstream channel, bypassing group isolation and per-channel model allowlists while triggering upstream-credential forwarding. The URL-parameter path now requires an admin, matching the token-suffix path.
  • Re-validate group / model allowlist on pinned channels (one-api#2410): even when an admin pins a channel, the distributor now re-checks that the channel actually serves the caller's user group and requested model before forwarding — defense in depth.
  • Stopped leaking access_token in admin user APIs (one-api#2425): the admin batch endpoints (GET /api/user/, GET /api/user/search) no longer return users' access_tokens, preventing token replay that could escalate to root (consistent with the existing single-user read path).
  • Added Channel.ContainsGroup / Channel.ContainsModel model helpers and regression tests covering all of the above.

✨ New Features

  • Reworked all four auth pages — Login, Register, Password Reset, and Password Reset Confirm — around a new AuthLayout (decorative gradient-orb background + branded logo slot), unified semantic headings (h1 + subtitle) and form aria-labels; tidied up forgot-link / form-alert styles so all four pages look and behave consistently.
  • Added Terms of Service (/terms) and Privacy Policy (/privacy) pages rendered via a new LegalLayout (top nav + legal document layout). Both routes are whitelisted in the router so visitors can reach them without first logging in.
  • Registration now requires checking "I agree to the Terms of Service and Privacy Policy" (with aria-label) before submitting, and the copy includes direct /terms / /privacy links.
  • Added an empty state to the Plans page so users see a friendly illustration + hint ("暂无可用套餐 / 请联系管理员配置套餐后再来查看") instead of a blank grid when no plans are configured.
  • Added Channel.ContainsGroup / Channel.ContainsModel allowlist helpers (comma-split, exact match; empty config allows all), providing the reusable building block for downstream auth and re-validation logic.
  • Embedded-theme check at startup: added common.ValidateEmbeddedTheme(buildFS, themesRoot, theme) and common.ListEmbeddedThemes(buildFS, themesRoot) helpers. main.go invokes the validator right after logging using theme <name>; if the configured theme is not embedded in the binary, it emits a loud [ERROR] that names the missing web/build/<theme>/index.html, lists every theme that IS embedded, and points at three fixes (update options.theme / adjust the THEME env / add the theme to web/THEMES and rebuild). The check is non-fatal — the server still starts so the operator can fix the misconfiguration — but it puts the "blank admin page" failure mode straight into the log.

🐛 Bug Fixes

  • Removed an extra page-container padding in the Orders page so its margins match the other pages.
  • Fixed the security issue where ordinary users could bypass group isolation / model allowlists by pinning an upstream channel (see Security).
  • Fixed the security issue where admin user list / search responses could expose access_token (see Security).
  • router/web.go no longer silently swallows the error from reading web/build/<theme>/index.html; it now logs theme %q is not embedded ... via logger.SysError as a safety net, so the failure is still visible even if SetWebRouter is ever reached before the startup check (tests, future refactors).

📚 Documentation

  • README i18n now covers 8 languages: added German, Arabic, Korean, Russian, Japanese and Traditional Chinese READMEs, and updated the language navigation in both the main and English READMEs.

🔧 Tooling / CI

  • Added three groups of regression tests: middleware channel-pinning auth (middleware/auth_test.go), user-list access_token redaction (controller/user_test.go), and model allowlist helpers (model/channel_contains_test.go).
  • Added 5 unit tests for the embedded-theme validator (common/embed_theme_test.go, with synthetic fixtures in common/embed_theme_testdata/): list themes, theme present, theme missing, empty theme, and trailing-slash tolerance on the themes root.

⚠️ Upgrade Notes

  • Code/frontend-only release — no database migration; safe to upgrade in place.
  • Login / register / password-reset pages have been redesigned; please clear the browser cache or verify in an incognito window after upgrading.
  • If you relied on anonymous "pin channel by URL parameter" proxying in open-registration deployments, note that this is no longer allowed: only admins can pin a channel (via URL parameter or token suffix).
  • Stale options.theme from older releases will now be caught: when upgrading from an older one-api / one-api-pro that shipped the legacy default theme, you may see [ERROR] theme "default" is not embedded in this binary ... at startup — that means the MySQL options table still holds the old theme=default row, which overrides the env default. Run UPDATE options SET value='default-pro' WHERE \key`='theme';` (or delete the row) and restart.