41 lines · 2.1 KB
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<void> {
  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<RepoRow, "owner_id">,
  target: { id: string },
  viewer: Viewer,
): Promise<void> {
  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");
  }
}