import { forbidden } from "./errors"; import { canAdministerAccount, canCreateInAccount, hasPermission, repoPermission } from "./repos"; import type { RepoRow, Viewer } from "~/lib/types"; /** * Who may fork `source` into `target` (GIT-A8 / AZ-A7). Public repositories fork like before. A private * repository is only forkable by writers, or by readers when its owner enabled `allow_forking`; and the * copy may only land in the forker's own account or in an organization they administer, so no plain org * member can hand a private history to every member of an unrelated org. */ export async function assertCanFork(source: RepoRow, target: { id: string }, viewer: Viewer): Promise { if (!(await canCreateInAccount(target.id, viewer))) throw forbidden("You cannot create repositories here"); if (source.visibility === "public") return; const permission = await repoPermission(source, viewer); if (!hasPermission(permission, "write") && !(source.allow_forking === 1 && hasPermission(permission, "read"))) { throw forbidden("Forking this private repository is not allowed"); } if (target.id !== viewer.id && !(await canAdministerAccount(target.id, viewer))) { throw forbidden("Private repositories can only be forked into your account or an organization you administer"); } } /** * Who may move `repo` into `target` (authz-P1/P2). Transfers need admin on the current owner (a * collaborator's repo-level admin is not enough to give the repository away) and on the target, since a * plain org member landing a repository in an org hands it to every member. The admin requirement on the * target also satisfies the private-fork placement rule of `assertCanFork`. */ export async function assertCanTransfer( repo: Pick, target: { id: string }, viewer: Viewer, ): Promise { if (!(await canAdministerAccount(repo.owner_id, viewer))) { throw forbidden("Only the owner can transfer this repository"); } if (!(await canAdministerAccount(target.id, viewer))) { throw forbidden("Repositories can only be transferred to your account or an organization you administer"); } }