-
Notifications
You must be signed in to change notification settings - Fork 502
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add permissions for ratelimit overrides #2126
base: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
📝 Walkthrough📝 WalkthroughWalkthroughThis pull request introduces several new API routes for managing rate limit overrides, including the ability to set, delete, and list these overrides. The routes require bearer authentication and handle various request and response formats, including JSON payloads. Additionally, it updates the analytics logging for rate limit actions and introduces new permissions related to these functionalities. The changes also include modifications to the existing Changes
Possibly related PRs
Suggested labels
Suggested reviewers
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (4)
🧰 Additional context used🪛 Biomeapps/api/src/routes/v1_ratelimit_setOverride.ts[error] 116-116: This variable is unused. Unused variables usually are result of incomplete refactoring, typos and other source of bugs. (lint/correctness/noUnusedVariables) 🔇 Additional comments (7)apps/api/src/routes/v1_ratelimit_setOverride.ts (7)
As mentioned in the past review, the identifier description should include a link explaining how wildcards work. While the current description mentions wildcards, it would be more helpful to include specific documentation.
The type exports are properly structured and provide good type safety for the API contract.
The audit logging code is duplicated between
Fix incorrect permissions in query As noted in the past review, the query includes unnecessary permissions. Remove buildUnkeyQuery(({ or }) =>
or(
"*",
- "ratelimit.*.set_override",
- "ratelimit.*.read_override",
+ "ratelimit.*.set_override"
),
),
Add namespace ownership verification Critical security issue: The code needs to verify that the requested namespace belongs to the authenticated workspace before allowing operations. Currently, users could potentially insert overrides into other workspaces' namespaces by guessing the ID. Add verification before the transaction: const { db, analytics } = c.get("services");
+ const namespace = await db.primary
+ .select()
+ .from(schema.namespaces)
+ .where(sql`id = ${req.namespaceId} AND workspace_id = ${auth.authorizedWorkspaceId}`)
+ .limit(1)
+ .execute();
+
+ if (!namespace.length) {
+ throw new Error("Namespace not found or not authorized");
+ }
await db.primary.transaction(async (tx) => {
Fix unreliable operation type detection The current method of checking As suggested in the past review, fetch the override first to determine if it's a create or update operation: await db.primary.transaction(async (tx) => {
+ const existing = await tx
+ .select()
+ .from(schema.ratelimitOverrides)
+ .where(sql`namespace_id = ${req.namespaceId} AND identifier = ${req.identifier}`)
+ .limit(1)
+ .execute();
+ const isUpdate = existing.length > 0;
+ const overrideId = isUpdate ? existing[0].id : newId("ratelimitOverride");
const res = await tx
.insert(schema.ratelimitOverrides)
.values({
- id: newId("ratelimitOverride"),
+ id: overrideId,
// ... other fields
})
.onDuplicateKeyUpdate({
set: {
// ... update fields
},
});
- let auditType: "ratelimitOverride.create" | "ratelimitOverride.update" =
- "ratelimitOverride.create";
- if (!res.statement.startsWith("insert")) {
- auditType = "ratelimitOverride.update";
- } 🧰 Tools🪛 Biome[error] 116-116: This variable is unused. Unused variables usually are result of incomplete refactoring, typos and other source of bugs. (lint/correctness/noUnusedVariables)
Add updatedAt timestamp As noted in the past review, the .onDuplicateKeyUpdate({
set: {
limit: req.limit,
duration: req.duration,
async: req.async,
+ updatedAt: new Date(),
},
}), Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Thank you for following the naming conventions for pull request titles! 🙏 |
…-custom-override-permissions
….com/unkeyed/unkey into eng-1336-custom-override-permissions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- apps/api/src/routes/v1_ratelimit_listOverrides.ts (1 hunks)
🧰 Additional context used
🔇 Additional comments (4)
apps/api/src/routes/v1_ratelimit_listOverrides.ts (4)
59-62
: LGTM: Type exports are well-definedThe type exports for the route and response are correctly defined, providing good type safety for consumers of this module.
63-71
: LGTM: Route handler setup and authenticationThe route handler is correctly set up using
app.openapi
, and the authentication check is properly implemented usingrootKeyAuth
with the correct permissions.
43-47
:⚠️ Potential issueConsider implementing proper pagination logic
The response schema includes a
cursor
field for pagination, but the current implementation doesn't handle pagination in the query or modify the dataset based on pagination parameters. This may lead to confusion for API consumers.Consider implementing pagination by:
- Accepting
limit
andcursor
parameters in the request query.- Modifying the database queries to use these parameters.
- Updating the response to include the next cursor only if there are more results.
If pagination is not required, update the response schema to remove the
cursor
field to avoid misleading API consumers.
116-116
: 🛠️ Refactor suggestionSimplify the handling of the
async
fieldThe current code uses
r.async ?? undefined
, which is redundant. Sincer.async
will already beundefined
if not present, you can simplify this line.Apply this change:
- async: r.async ?? undefined, + async: r.async,
if (!identifier) { | ||
const overrides = await db.readonly.query.ratelimitOverrides.findMany({ | ||
where: (table, { eq, and }) => | ||
and( | ||
eq(table.workspaceId, auth.authorizedWorkspaceId), | ||
eq(table.namespaceId, namespaceId), | ||
), | ||
with: { | ||
namespace: true, | ||
}, | ||
}); | ||
|
||
return c.json({ | ||
overrides: overrides.map((r) => ({ | ||
id: r.id, | ||
namespace: r.namespace.name, | ||
identifier: r.identifier, | ||
limit: r.limit, | ||
duration: r.duration, | ||
async: r.async, | ||
})), | ||
total: overrides.length, | ||
cursor: overrides.at(-1)?.id ?? undefined, | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implement proper pagination logic
While the code sets a cursor to the ID of the last override, there's no actual pagination logic implemented. This means that subsequent requests won't use this cursor to fetch the next set of results.
Consider implementing proper pagination:
- Accept a
cursor
parameter in the request. - Use this cursor to filter results in the database query.
- Limit the number of results returned (e.g., using a
LIMIT
clause). - Only include the
cursor
in the response if there are more results to fetch.
This will ensure that clients can efficiently paginate through large sets of overrides.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chronark, understood.
(_/)
( •_•)
/>💧
const overrides = await db.readonly.query.ratelimitOverrides.findMany({ | ||
where: (table, { eq, and }) => | ||
and( | ||
eq(table.workspaceId, auth.authorizedWorkspaceId), | ||
eq(table.namespaceId, namespaceId), | ||
eq(table.identifier, identifier), | ||
), | ||
with: { | ||
namespace: true, | ||
}, | ||
}); | ||
|
||
return c.json({ | ||
overrides: overrides.map((r) => ({ | ||
id: r.id, | ||
namespace: r.namespace.name, | ||
identifier: r.identifier, | ||
limit: r.limit, | ||
duration: r.duration, | ||
async: r.async ?? undefined, | ||
})), | ||
total: overrides.length, | ||
cursor: overrides.at(-1)?.id ?? undefined, | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implement consistent pagination logic
Similar to the case without an identifier, this section lacks proper pagination logic. To ensure consistency and efficiency, implement pagination here as well:
- Accept and use a
cursor
parameter in the request. - Modify the database query to use this cursor for filtering.
- Limit the number of results returned.
- Include the
cursor
in the response only if there are more results to fetch.
This will provide a consistent pagination experience across both cases (with and without identifier).
…-custom-override-permissions
example: "eyJrZXkiOiJrZXlfMTIzNCJ9", | ||
}), | ||
total: z.number().int().openapi({ | ||
description: "The total number of keys for this api", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrong description
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad missed that, Changed to "The total number of overrides for the namespace and optional identifier"
buildUnkeyQuery(({ or }) => or("*", "ratelimit.*.read_override")), | ||
); | ||
if (!identifier) { | ||
const overrides = await db.readonly.query.ratelimitOverrides.findMany({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this doesn't use the cursor for pagination
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we also want a page size limit
check out how we did it for listing api keys, or ping me and we can talk it through
duration: r.duration, | ||
async: r.async, | ||
})), | ||
total: overrides.length, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this needs to load all overrides in the namespace, not just the ones you return
let's say a user has 200 overrides.
We return the first 100 in the overrides
field, but also let them know that in total there are 200
duration: req.duration, | ||
async: req.async, | ||
}, | ||
}); | ||
let auditType: "ratelimitOverride.create" | "ratelimitOverride.update" = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should fetch the override, mutate it and then write it back to the db inside the transaction.
that also solves this issue becaues we clearly know whether it's a create or update
Co-authored-by: Andreas Thomas <dev@chronark.com>
…-custom-override-permissions
…-custom-override-permissions
What does this PR do?
Fixes # (issue)
eng-1336
Type of change
How should this be tested?
Check if I understood the assignment properly.
Checklist
Required
pnpm build
pnpm fmt
console.logs
git pull origin main
Appreciated
Summary by CodeRabbit
Release Notes
New Features
/v1/ratelimit.setOverride
/v1/ratelimit.deleteOverride
/v1/ratelimit.listOverrides
set_override
,read_override
,delete_override
.Enhancements
Bug Fixes