fix(permissions): require explicit organization share for video downloads - #2052
fix(permissions): require explicit organization share for video downloads#2052wasim-builds wants to merge 6 commits into
Conversation
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
| import { canUserDownloadVideo } from "@/lib/video-download-permissions"; | ||
|
|
||
| describe("canUserDownloadVideo", () => { | ||
| it("grants download access to the video owner", async () => { |
There was a problem hiding this comment.
Permission change remains untested
The sole test uses identical userId and ownerId values, so it returns through the unchanged owner shortcut without exercising the new organization-sharing check. Add cases proving that an organization member is denied without a sharedVideos entry and allowed with one, otherwise the regression addressed by this PR can return without failing this suite.
Knowledge Base Used: Web App (apps/web)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/__tests__/unit/video-download-permissions.test.ts
Line: 5
Comment:
**Permission change remains untested**
The sole test uses identical `userId` and `ownerId` values, so it returns through the unchanged owner shortcut without exercising the new organization-sharing check. Add cases proving that an organization member is denied without a `sharedVideos` entry and allowed with one, otherwise the regression addressed by this PR can return without failing this suite.
**Knowledge Base Used:** [Web App (apps/web)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/web-app.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| .where(eq(sharedVideos.videoId, videoId)); | ||
|
|
||
| const orgIds = [orgId, ...sharedOrgs.map((org) => org.organizationId)]; | ||
| if (sharedOrgs.length > 0) { |
There was a problem hiding this comment.
This change makes the orgId arg effectively unused in this function. If the signature is meant to stay stable, consider aliasing it to _orgId (or dropping it entirely) so Biome doesn’t flag an unused variable and the API stays less confusing.
| import { canUserDownloadVideo } from "@/lib/video-download-permissions"; | ||
|
|
||
| describe("canUserDownloadVideo", () => { | ||
| it("grants download access to the video owner", async () => { |
There was a problem hiding this comment.
This test currently only covers the owner short-circuit. Since this PR changes org-level download behavior, it’d be useful to add a case that exercises the new shared_videos gating (member of org + explicit share => allow; member of org without share => deny), to prevent regressions.
| } | ||
| const set = this.listeners.get(event); | ||
| if (set) set.add(handler as EventHandler<keyof RecorderEventMap>); | ||
| if (set) set.add(handler as any); |
There was a problem hiding this comment.
Using as any here drops a lot of type safety and can also make add/remove symmetry less obvious. You can keep the set typed and just normalize the handler once:
| if (set) set.add(handler as any); | |
| const set = this.listeners.get(event); | |
| if (!set) return () => {}; | |
| const typedHandler = handler as EventHandler<keyof RecorderEventMap>; | |
| set.add(typedHandler); | |
| return () => { | |
| set.delete(typedHandler); | |
| }; |
| db: () => ({ | ||
| select: () => ({ | ||
| from: (table: unknown) => ({ | ||
| where: () => { |
There was a problem hiding this comment.
This db() mock ignores the where(...) conditions entirely, so the “explicitly shared org” test is really only asserting the sharedOrgs.length > 0 gating. If you want the test to actually protect the inArray(...orgIds) behavior, consider making the mock return org membership conditionally (or add a mismatch case where sharedVideos contains a different orgId and expect false).
| } | ||
| const set = this.listeners.get(event); | ||
| if (set) set.add(handler as EventHandler<keyof RecorderEventMap>); | ||
| const castHandler = handler as unknown as EventHandler<keyof RecorderEventMap>; |
There was a problem hiding this comment.
Since the Set is always created right above, you can simplify this a bit and avoid the extra Map.get(...) in the unsubscribe closure:
| const castHandler = handler as unknown as EventHandler<keyof RecorderEventMap>; | |
| const set = this.listeners.get(event)!; | |
| const castHandler = handler as unknown as EventHandler<keyof RecorderEventMap>; | |
| set.add(castHandler); | |
| return () => set.delete(castHandler); |
| } from "@cap/database/schema"; | ||
| import type { Organisation, User, Video } from "@cap/web-domain"; | ||
| import { beforeEach, describe, expect, it, vi } from "vitest"; | ||
| import { canUserDownloadVideo } from "../../../lib/video-download-permissions"; |
There was a problem hiding this comment.
This relative path resolves to apps/lib/video-download-permissions, which doesn't exist — the source file is at apps/web/lib/. The __tests__/unit/ directory is only two levels below apps/web/, so you'd need ../../lib/... for a relative path. Since @/lib is already aliased in both tsconfig.json and vitest.config.ts (and used by other unit tests like roles-permissions.test.ts), reverting to the alias is probably cleanest:
| import { canUserDownloadVideo } from "../../../lib/video-download-permissions"; | |
| import { canUserDownloadVideo } from "@/lib/video-download-permissions"; |
Fixes #2037 by requiring an explicit
sharedVideosentry for organization-level video download permission checks, aligning it with the view policy and space-level download checks.Greptile Summary
This PR narrows organization-level video download access to explicitly shared organizations.
Confidence Score: 4/5
The implementation appears safe to merge, with the non-blocking caveat that its central permission regression is not covered by the added test.
The authorization change implements the stated explicit-share requirement, but the only new test exits through the unchanged owner shortcut and would not detect a regression in organization-level checks.
Files Needing Attention: apps/web/tests/unit/video-download-permissions.test.ts
Important Files Changed
Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "fix(permissions): require explicit organ..." | Re-trigger Greptile
Context used: