Skip to content

core: group api#1168

Open
renbaoshuo wants to merge 4 commits into
hydro-dev:masterfrom
renbaoshuo:group-api
Open

core: group api#1168
renbaoshuo wants to merge 4 commits into
hydro-dev:masterfrom
renbaoshuo:group-api

Conversation

@renbaoshuo

@renbaoshuo renbaoshuo commented May 26, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added group search with case-insensitive matching and optional result limit (max 100).
    • Support filtering groups by specific names and listing groups for a specific user.
  • Improvements

    • Queries now apply filtering server-side for faster, more efficient results.
  • Bug Fixes

    • Removed previously returned spurious extra group entries.

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 621d8dce-ac01-49ca-b7de-f279f3313fa3

📥 Commits

Reviewing files that changed from the base of the PR and between f019d35 and 201a8c9.

📒 Files selected for processing (1)
  • packages/hydrooj/src/model/user.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/hydrooj/src/model/user.ts

Walkthrough

DomainApi.groups argument schema now accepts domainId, uid, names, search, and limit. The resolver checks permissions and calls UserModel.listGroup(domainId, uid, names, search, limit) — when search is provided and limit omitted it forwards limit=10; otherwise limit is not forwarded. UserModel.listGroup builds a MongoDB filter from domainId and conditionally includes uid(s), name {$in: names}, and a case-insensitive escaped regex for search, and applies limit when present.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • undefined-moe
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'core: group api' is related to the changeset, which updates the DomainApi.groups endpoint and UserModel.listGroup method for improved group retrieval functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/hydrooj/src/model/user.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/hydrooj/src/handler/domain.ts`:
- Around line 447-455: The permission checks currently use ctx.user but the
resource being queried is args.domainId; update the code so authorization is
performed against the target domain's context before returning groups: obtain
the domain-specific user/authorization context for args.domainId (e.g., call a
helper like ctx.forDomain(args.domainId) / ctx.getDomainUser(args.domainId) or
use an existing domain authorization method), then call hasPerm/hasPriv on that
domain-scoped user instead of ctx.user, and only after that call user.listGroup,
user.searchGroups or user.listGroup with args.domainId; apply the same change to
the other similar block that calls user.listGroup/user.searchGroups to ensure
checks use the target domain context.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f4e0a869-3bfa-4dea-be58-969853e8808d

📥 Commits

Reviewing files that changed from the base of the PR and between cefe2d6 and 2664562.

📒 Files selected for processing (2)
  • packages/hydrooj/src/handler/domain.ts
  • packages/hydrooj/src/model/user.ts

Comment on lines 447 to +455
if (!ctx.user.hasPerm(PERM.PERM_VIEW) && !ctx.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) throw new PermissionError(PERM.PERM_VIEW);
const groups = await user.listGroup(args.domainId);
if (args.names?.length) {
return groups.filter((g) => args.names.includes(g.name));
return user.listGroup(args.domainId, undefined, args.names);
}
if (args.search) {
const searchLower = args.search.toLowerCase();
return groups.filter((g) => g.name.toLowerCase().includes(searchLower));
return user.searchGroups(args.domainId, args.search, args.limit ?? 10);
}
return groups;
return user.listGroup(args.domainId);
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Authorize using the target domain context before returning groups.

Line 447 and Line 463 validate permissions on ctx.user, but the queried resource is args.domainId. This can expose another domain’s groups/user-groups when a caller passes a different domainId.

🔒 Suggested fix
 async (ctx, args) => {
-    if (!ctx.user.hasPerm(PERM.PERM_VIEW) && !ctx.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) throw new PermissionError(PERM.PERM_VIEW);
+    const targetUser = await user.getById(args.domainId, ctx.user._id);
+    if (!targetUser || (!targetUser.hasPerm(PERM.PERM_VIEW) && !targetUser.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN))) {
+        throw new PermissionError(PERM.PERM_VIEW);
+    }
     if (args.names?.length) {
         return user.listGroup(args.domainId, undefined, args.names);
     }
@@
 async (ctx, args) => {
-    if (!ctx.user.hasPerm(PERM.PERM_VIEW) && !ctx.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) throw new PermissionError(PERM.PERM_VIEW);
+    const targetUser = await user.getById(args.domainId, ctx.user._id);
+    if (!targetUser || (!targetUser.hasPerm(PERM.PERM_VIEW) && !targetUser.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN))) {
+        throw new PermissionError(PERM.PERM_VIEW);
+    }
     return user.listGroup(args.domainId, args.uid);
 },

Also applies to: 463-465

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrooj/src/handler/domain.ts` around lines 447 - 455, The
permission checks currently use ctx.user but the resource being queried is
args.domainId; update the code so authorization is performed against the target
domain's context before returning groups: obtain the domain-specific
user/authorization context for args.domainId (e.g., call a helper like
ctx.forDomain(args.domainId) / ctx.getDomainUser(args.domainId) or use an
existing domain authorization method), then call hasPerm/hasPriv on that
domain-scoped user instead of ctx.user, and only after that call user.listGroup,
user.searchGroups or user.listGroup with args.domainId; apply the same change to
the other similar block that calls user.listGroup/user.searchGroups to ensure
checks use the target domain context.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/hydrooj/src/model/user.ts (1)

490-494: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply the active filters and cap to the synthetic uid group too.

The fallback { name: uid.toString() } entry is appended after the Mongo query, so search, names, and limit are not enforced on that item. For example, a filtered search can still return the uid group even when it does not match, and the final array can exceed limit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrooj/src/model/user.ts` around lines 490 - 494, The synthetic
fallback group currently unconditionally pushes an entry (using uid, groups,
ObjectId, domainId, uids, name) after the Mongo query, bypassing the same
search/names filtering and the limit cap; modify the logic so you only add this
synthetic group if it passes the same filters (search/names predicates used for
the query) and only if groups.length is still below the requested limit, i.e.,
evaluate the same match predicate(s) against the synthetic { _id: new
ObjectId(), domainId, uids: [uid], name: uid.toString() } and push it only when
it matches and groups.length < limit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/hydrooj/src/model/user.ts`:
- Around line 485-486: The code overwrites filter.name when both names and
search are provided; update the logic so both constraints are applied instead of
dropped: if both names and search are present, set filter.$and = [{ name: { $in:
names } }, { name: { $regex: escapeRegExp(search), $options: 'i' } }], otherwise
keep the existing single-condition assignments (the identifiers to change are
filter, names, search, and escapeRegExp).
- Around line 487-488: The current check uses "if (limit)" so limit = 0 is
treated as no limit; update the code that builds the Mongo cursor (the
collGroup.find(filter) and subsequent .limit() call) to treat non-positive
values as invalid by checking if (typeof limit === 'number' && limit > 0) before
calling cursor.limit(limit), or clamp negative/zero to a safe minimum (e.g., 1)
or explicitly throw/bail; ensure the change references the same
variables/functions (collGroup.find, cursor, limit, .limit()) so callers cannot
supply 0 to create an unbounded query.

---

Outside diff comments:
In `@packages/hydrooj/src/model/user.ts`:
- Around line 490-494: The synthetic fallback group currently unconditionally
pushes an entry (using uid, groups, ObjectId, domainId, uids, name) after the
Mongo query, bypassing the same search/names filtering and the limit cap; modify
the logic so you only add this synthetic group if it passes the same filters
(search/names predicates used for the query) and only if groups.length is still
below the requested limit, i.e., evaluate the same match predicate(s) against
the synthetic { _id: new ObjectId(), domainId, uids: [uid], name: uid.toString()
} and push it only when it matches and groups.length < limit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2e4d3d6e-8c44-4ba3-a433-93f571493446

📥 Commits

Reviewing files that changed from the base of the PR and between 4be82ba and f019d35.

📒 Files selected for processing (2)
  • packages/hydrooj/src/handler/domain.ts
  • packages/hydrooj/src/model/user.ts

Comment thread packages/hydrooj/src/model/user.ts Outdated
Comment on lines +487 to +488
let cursor = collGroup.find(filter);
if (limit) cursor = cursor.limit(limit);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Treat 0 as invalid instead of "no limit".

This truthy check makes limit = 0 skip .limit() entirely, so a caller can turn the new search path into an unbounded query. Reject or clamp non-positive values before building the cursor.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrooj/src/model/user.ts` around lines 487 - 488, The current check
uses "if (limit)" so limit = 0 is treated as no limit; update the code that
builds the Mongo cursor (the collGroup.find(filter) and subsequent .limit()
call) to treat non-positive values as invalid by checking if (typeof limit ===
'number' && limit > 0) before calling cursor.limit(limit), or clamp
negative/zero to a safe minimum (e.g., 1) or explicitly throw/bail; ensure the
change references the same variables/functions (collGroup.find, cursor, limit,
.limit()) so callers cannot supply 0 to create an unbounded query.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant