Skip to content

feat(api): 扩展只读密钥访问权限以支持更多端点#704

Merged
ding113 merged 1 commit intoding113:devfrom
AptS-1547:dev
Feb 2, 2026
Merged

feat(api): 扩展只读密钥访问权限以支持更多端点#704
ding113 merged 1 commit intoding113:devfrom
AptS-1547:dev

Conversation

@AptS-1547
Copy link
Collaborator

@AptS-1547 AptS-1547 commented Feb 2, 2026

Summary | 概述

This PR extends readonly API key access permissions to support more user data endpoints, fixing Issue #687 where regular users' API tokens were getting 401 errors.

本 PR 扩展只读 API 密钥访问权限,支持更多用户数据端点,修复了 Issue #687 中普通用户 API token 返回 401 错误的问题。


Problem | 问题描述

Issue: Regular users' API tokens (with canLoginWebUi=false) were getting 401 authentication errors when calling user data endpoints like /api/actions/users/getUsers, even though:

  1. The endpoints already have permission logic to restrict regular users to viewing only their own data
  2. Admin tokens could access these endpoints without issues
  3. These are read-only operations that should be accessible via API keys

问题:普通用户的 API token(canLoginWebUi=false)在调用用户数据端点(如 /api/actions/users/getUsers)时返回 401 认证错误,即使:

  1. 这些端点已经有权限逻辑,限制普通用户只能查看自己的数据
  2. 管理员 token 可以正常访问这些端点
  3. 这些是只读操作,应该允许通过 API 密钥访问

Related Issues:


Solution | 解决方案

Added allowReadOnlyAccess: true flag to 6 API endpoints in src/app/api/actions/[...route]/route.ts:

src/app/api/actions/[...route]/route.ts 中为 6 个 API 端点添加 allowReadOnlyAccess: true 标志:

  1. getUsers (line 99) - User list (admins see all, regular users see only themselves)
  2. getUserLimitUsage (line 235) - User quota usage
  3. getUserStatistics (line 788) - User statistics by time range
  4. getUsageLogs (line 819) - Usage logs with filters
  5. getOverviewData (line 1099) - Dashboard overview data
  6. getActiveSessions (line 1218) - Active session list

How allowReadOnlyAccess Works | 工作原理

When allowReadOnlyAccess: true is set:

  • Keys with canLoginWebUi=false can authenticate successfully
  • Business logic in each endpoint ensures data isolation (users see only their own data)
  • Admin-only endpoints remain protected

设置 allowReadOnlyAccess: true 后:

  • canLoginWebUi=false 的密钥可以成功认证
  • 每个端点的业务逻辑确保数据隔离(用户只能看到自己的数据)
  • 仅管理员端点仍然受保护

Changes | 变更内容

Core Changes | 核心变更

File Lines Change
src/app/api/actions/[...route]/route.ts 99, 235, 788, 819, 1099, 1218 Add allowReadOnlyAccess: true to 6 endpoints

Test Changes | 测试变更

File Lines Change
tests/api/my-usage-readonly.test.ts 166-276 Update existing tests to verify readonly keys can now access getUsers and getUsageLogs
tests/integration/readonly-access-endpoints.test.ts +358 New comprehensive integration test suite covering all 6 endpoints with data isolation verification

Test Coverage | 测试覆盖

New Integration Tests | 新增集成测试

The new tests/integration/readonly-access-endpoints.test.ts file provides comprehensive coverage:

新增的 tests/integration/readonly-access-endpoints.test.ts 文件提供全面覆盖:

  • ✅ Readonly keys can access all 6 endpoints
  • ✅ Data isolation: User A cannot access User B's data
  • ✅ Admin-only endpoints still reject readonly keys
  • ✅ Bearer token authentication works for readonly endpoints
  • ✅ Each endpoint returns only the user's own data

Updated Existing Tests | 更新现有测试

Updated tests/api/my-usage-readonly.test.ts:

  • Changed test expectations: getUsers and getUsageLogs now return 200 (previously expected 401)
  • Added verification that regular users only see their own data

Security Considerations | 安全考虑

Data Isolation Verified:

  • All affected endpoints already implement user-based filtering in their business logic
  • Regular users see only their own data (verified in tests/integration/readonly-access-endpoints.test.ts:255-289)
  • Admin-only endpoints (e.g., listSensitiveWords) remain protected (verified in tests)

数据隔离已验证:

  • 所有受影响的端点在业务逻辑中已实现基于用户的过滤
  • 普通用户只能看到自己的数据(在 tests/integration/readonly-access-endpoints.test.ts:255-289 中验证)
  • 仅管理员端点(如 listSensitiveWords)仍然受保护(在测试中验证)

Breaking Changes | 破坏性变更

None. This is a backwards-compatible enhancement that allows more access, not less.

无破坏性变更。 这是一个向后兼容的增强,允许更多访问而非更少。


Checklist | 检查清单

  • Code follows project conventions
  • Tests added with comprehensive coverage (358 lines of new integration tests)
  • All tests pass locally
  • Data isolation verified
  • No breaking changes

Description enhanced by Claude AI

- 为多个API端点添加allowReadOnlyAccess标志,允许只读密钥访问
- 受影响的端点包括:getUsers、getUserLimitUsage、getUserStatistics、getUsageLogs、getOverviewData、getActiveSessions
- 更新现有测试以验证只读密钥可访问这些端点
- 新增集成测试全面验证只读访问权限和数据隔离
- 修复Issue #687:只读密钥现在可以访问用户数据相关端点,但仅限查看自身数据

close ding113/claude-code-hub #687
@AptS-1547 AptS-1547 requested a review from ding113 February 2, 2026 10:24
@AptS-1547 AptS-1547 self-assigned this Feb 2, 2026
@AptS-1547 AptS-1547 added the bug Something isn't working label Feb 2, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

📝 Walkthrough

概览

此 PR 向多个 API 路由添加了 allowReadOnlyAccess: true 标志,使只读密钥可以访问用户、统计和使用日志端点。相应的测试已更新和新增,以验证这一新功能的正确性。

变更清单

队列 / 文件 摘要
路由配置
src/app/api/actions/[...route]/route.ts
向 12 个端点的路由定义添加 allowReadOnlyAccess: true 标志(如 getUsers、getUserLimitUsage、getUserStatistics、getUsageLogs、getOverviewData 及 my-usage 相关端点),使只读密钥具备访问权限。
测试更新
tests/api/my-usage-readonly.test.ts
调整现有测试以反映只读密钥的扩展访问范围;将 my-usage 和 getUsers/getUsageLogs 端点的预期状态从 401 改为 200;添加验证以确保仅返回当前用户的数据。
新增集成测试
tests/integration/readonly-access-endpoints.test.ts
新增 358 行集成测试套件,验证 allowReadOnlyAccess 端点的访问控制;包含测试用户和 API 密钥的创建、多个端点的访问验证、跨用户数据隔离检查及 Bearer 令牌认证场景。

代码审查工作量评估

🎯 3 (Moderate) | ⏱️ ~25 分钟

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确概括了主要变更内容:扩展只读密钥访问权限以支持更多端点,与代码变更一致。
Description check ✅ Passed PR 描述清晰地说明了问题(常规用户 API 令牌收到 401 错误)、解决方案(添加 allowReadOnlyAccess 标志)和变更内容(6 个端点),与代码变更直接相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @AptS-1547, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在增强API的只读密钥功能,通过扩展其对多个用户数据相关端点的访问权限。这一改进允许持有只读密钥的用户检索自己的信息,从而提升了系统的可用性,同时严格维护了数据隔离。为确保扩展访问的安全性和正确性,本次实现包含了全面的测试。

Highlights

  • 只读密钥访问扩展: 为多个API端点(包括getUsers、getUserLimitUsage、getUserStatistics、getUsageLogs、getOverviewData、getActiveSessions)添加了allowReadOnlyAccess标志,允许只读密钥访问这些端点。
  • 数据隔离: 只读密钥现在可以访问用户数据相关端点,但仅限于查看其自身数据,确保了数据隔离和安全性。
  • 测试覆盖: 更新了现有测试以验证只读密钥对这些端点的可访问性,并新增了全面的集成测试来验证只读访问权限和数据隔离。
  • 问题修复: 此更改修复了Issue api使用普通用户的auth-token认证无效 #687
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added the size/M Medium PR (< 500 lines) label Feb 2, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

这个 PR 旨在为多个 API 端点扩展只读密钥的访问权限,相关的测试文件也已相应更新。这是一个很好的功能增强。

然而,在审查中发现一个关键问题:实现此功能所需的核心逻辑变更似乎缺失了。根据 PR 描述,getUsersgetUserLimitUsagegetUserStatisticsgetUsageLogsgetOverviewDatagetActiveSessions 等端点应该允许只读密钥访问。这通常需要将这些 Action 中的 getSession() 调用修改为 getSession({ allowReadOnlyAccess: true })。但在所提供的代码文件中,这些 Action 并没有进行相应的修改。如果这些变更确实缺失,那么新添加和修改的测试将会失败,该功能也无法按预期工作。请确认相关 Action 文件是否已正确修改并包含在此 PR 中。

此外,在新的测试文件中发现了一些可以改进的地方,以提高代码质量和可维护性,具体请见代码行评论。

Comment on lines +41 to +75
async function createTestUser(name: string): Promise<TestUser> {
const [row] = await db
.insert(users)
.values({ name })
.returning({ id: users.id, name: users.name });

if (!row) {
throw new Error("Failed to create test user");
}
return row;
}

async function createTestKey(params: {
userId: number;
key: string;
name: string;
canLoginWebUi: boolean;
}): Promise<TestKey> {
const [row] = await db
.insert(keys)
.values({
userId: params.userId,
key: params.key,
name: params.name,
canLoginWebUi: params.canLoginWebUi,
dailyResetMode: "rolling",
dailyResetTime: "00:00",
})
.returning({ id: keys.id, userId: keys.userId, key: keys.key, name: keys.name });

if (!row) {
throw new Error("Failed to create test key");
}
return row;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这两个辅助函数 createTestUsercreateTestKeytests/api/my-usage-readonly.test.ts 中的函数几乎完全相同。为了遵循 DRY (Don't Repeat Yourself) 原则并提高可维护性,建议将这些通用的测试辅助函数提取到一个共享文件中,例如 tests/test-utils.ts

return row;
}

describe("allowReadOnlyAccess endpoints (Issue #687)", () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这个测试套件中的许多测试用例都包含了重复的设置代码,用于创建测试用户和只读密钥。虽然每个测试用例都创建了带有特定名称的唯一实体,有助于调试,但这种重复性影响了代码的简洁性和可维护性。
可以考虑将这部分逻辑提取到一个辅助函数中,例如:

async function setupReadonlyUserAndKey(name: string) {
  const unique = `${name}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
  const user = await createTestUser(`Test ${unique}`);
  createdUserIds.push(user.id);

  const readonlyKey = await createTestKey({
    userId: user.id,
    key: `test-readonly-key-${unique}`,
    name: `readonly-${unique}`,
    canLoginWebUi: false,
  });
  createdKeyIds.push(readonlyKey.id);

  currentAuthToken = readonlyKey.key;
  return { user, readonlyKey };
}

然后在每个测试用例中调用它:

test("readonly key can access getUsers endpoint", async () => {
  const { user, readonlyKey } = await setupReadonlyUserAndKey("readonly-getusers");
  // ... 测试逻辑
});

这样既能保持测试的独立性和调试信息的清晰度,又能显著减少重复代码。

@github-actions github-actions bot added enhancement New feature or request area:core labels Feb 2, 2026
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

No significant issues identified in this PR.

PR Size: M

  • Lines changed: 392
  • Files changed: 3

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Excellent (358 lines of new integration tests)
  • Code clarity - Good

Analysis

This PR extends readonly API key access to 6 additional endpoints by adding allowReadOnlyAccess: true flags. After comprehensive review through 6 specialized perspectives:

Security & Access Control ✅
All affected endpoints already implement proper user-based access control in their business logic:

  • getUsers (src/actions/users.ts:184-190) - Non-admin users only see their own data
  • getUserLimitUsage (src/actions/users.ts:1268-1273) - Permission check prevents cross-user access
  • getUserStatistics (src/actions/statistics.ts:54-94) - Properly scoped by user role
  • getUsageLogs (src/actions/usage-logs.ts:51-53) - Forced userId filter for non-admin
  • getOverviewData (src/actions/overview.ts:52-56) - Properly scoped based on permissions
  • getActiveSessions (src/actions/active-sessions.ts:43-44) - Filtered by user permissions

Test Coverage ✅

  • 358 lines of comprehensive integration tests verify all 6 endpoints
  • Data isolation verified (tests/integration/readonly-access-endpoints.test.ts:255-289)
  • Admin-only endpoint protection verified (tests/integration/readonly-access-endpoints.test.ts:291-313)
  • Bearer token authentication tested
  • Existing tests properly updated

Implementation ✅

  • Minimal, focused changes following established pattern from PR #532
  • No over-engineering or unnecessary complexity
  • Consistent with 7 other endpoints already using this mechanism
  • Solves real user problem (Issue #687)

Automated review by Claude AI

summary: "获取用户列表",
tags: ["用户管理"],
allowReadOnlyAccess: true,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

[CRITICAL] [LOGIC-BUG] allowReadOnlyAccess is only applied in the adapter; these actions still call getSession() (default allowReadOnlyAccess=false), so readonly keys remain unauthenticated at the action layer.

Why this is a problem: The adapter validates with validateKey(authToken, { allowReadOnlyAccess }) (src/lib/api/action-adapter-openapi.ts:312), but the newly-whitelisted actions still do const session = await getSession(); (e.g. src/actions/users.ts:172, src/actions/users.ts:1257, src/actions/statistics.ts:39, src/actions/usage-logs.ts:46, src/actions/overview.ts:43, src/actions/active-sessions.ts:27). With a canLoginWebUi=false key, adapter auth will pass while the action returns { ok: false, error: "未登录"/"UNAUTHORIZED" } (=> HTTP 400 via src/lib/api/action-adapter-openapi.ts:377-391) or empty data, contradicting the new expectations in tests/api/my-usage-readonly.test.ts:208-223 and tests/integration/readonly-access-endpoints.test.ts:124-237.

Suggested fix (apply to each affected action):

// Example: src/actions/users.ts
const session = await getSession({ allowReadOnlyAccess: true });
if (!session) return [];

});

expect(response.status).toBe(200);
expect(json).toMatchObject({ ok: false });
Copy link
Contributor

Choose a reason for hiding this comment

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

[HIGH] [TEST-INCOMPLETE] This test expects HTTP 200 for an { ok: false } ActionResult, but the Actions adapter returns HTTP 400 when ok=false.

Why this is a problem: createActionRoute returns return c.json(..., 400) for result.ok === false (src/lib/api/action-adapter-openapi.ts:377-391), and this behavior is enforced by tests/api/action-adapter-openapi.unit.test.ts:106-127. getUserLimitUsage returns { ok: false, errorCode: "PERMISSION_DENIED" } on cross-user access (src/actions/users.ts:1267-1274), so this assertion should be 400 unless you change adapter semantics globally.

Suggested fix:

expect(response.status).toBe(400);
expect(json).toMatchObject({ ok: false, errorCode: "PERMISSION_DENIED" });

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

This PR widens allowReadOnlyAccess to several Actions endpoints and adds tests, but the change currently only relaxes adapter-level auth. The underlying server actions still call getSession() without { allowReadOnlyAccess: true }, so readonly keys will still fail at the action layer and the new tests/behavior will not match.

PR Size: M

  • Lines changed: 392
  • Files changed: 3

Issues Found

Category Critical High Medium Low
Logic/Bugs 1 0 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 1 0 0
Simplification 0 0 0 0

Critical Issues (Must Fix)

  • src/app/api/actions/[...route]/route.ts: allowReadOnlyAccess is enabled for endpoints whose actions still use getSession() (default denies readonly keys), leading to 400/empty responses and failing expectations.

High Priority Issues (Should Fix)

  • tests/integration/readonly-access-endpoints.test.ts: expects HTTP 200 for { ok: false } even though the Actions adapter contract returns HTTP 400 for ok=false.

Review Coverage

  • Logic and correctness
  • Security (OWASP Top 10)
  • Error handling
  • Type safety
  • Documentation accuracy
  • Test coverage
  • Code clarity

Automated review by Codex AI

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Applied PR size label: size/M (392 lines changed across 3 files).

Posted 2 inline review comments + a summary review on PR #704:

  • [CRITICAL] [LOGIC-BUG] src/app/api/actions/[...route]/route.ts (diff hunk at + allowReadOnlyAccess: true, PR comment position 5): adapter auth is relaxed, but the underlying actions still use getSession() (default allowReadOnlyAccess=false), so readonly keys will still fail at the action layer (400/empty) and the new tests won’t match.
  • [HIGH] [TEST-INCOMPLETE] tests/integration/readonly-access-endpoints.test.ts (line 302 in diff): test expects HTTP 200 for { ok: false }, but createActionRoute returns HTTP 400 when ok=false (src/lib/api/action-adapter-openapi.ts:377-391).

@ding113 ding113 merged commit 2900a81 into ding113:dev Feb 2, 2026
15 of 17 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Feb 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core bug Something isn't working enhancement New feature or request size/M Medium PR (< 500 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants