gitea_team_rights: refuse a forge user that does not exist; keep ForgeRepository's doc comment on it (issue 1563)
mesh/delivery merged: source.main moved to novox/mesh-catalog@bf94af0a820f
mesh/check checking 36d59933239e on bf94af0a820f

This commit is contained in:
2026-10-27 21:54:51 +01:00
parent f8ddcc98f6
commit 36d5993323
3 changed files with 9 additions and 2 deletions
+1 -2
View File
@@ -9,6 +9,7 @@ import { ConfiguredToken, MintedToken, type TokenSource } from "./token.js";
import { MAIN_BRANCH, mainRuleBody, protectionBody, type BranchProtection, type ProtectionWanted } from "./protection.js";
import { Refused, checkSentPath, ruleCovers, pathSegment, pathSegments } from "./guard.js";
import { isCommit } from "./checks.js";
import type { ForgeTeam } from "./teamrights.js";
/** A repository's path on the forge, its owner and name each one segment (guard.ts pathSegment): uniform for every
* call, whether the names came from a caller or from the forge's own answer. */
@@ -930,8 +931,6 @@ interface AdminResponse {
}
/** A repository as the site administrator sees it: what saying why an account cannot read it needs (readall.ts). */
import type { ForgeTeam } from "./teamrights.js";
export interface ForgeRepository {
full_name: string;
owner: string;
+2
View File
@@ -24,6 +24,7 @@ export interface ForgeTeam {
export interface TeamForge {
teams(org: string): Promise<ForgeTeam[]>;
teamMembers(teamId: number): Promise<string[]>;
accountExists(user: string): Promise<boolean>;
everyRepository(): Promise<{ full_name: string; owner: string; name: string }[]>;
repositoryPermission(owner: string, repo: string, user: string): Promise<string>;
}
@@ -74,6 +75,7 @@ export interface UserRights {
* in and its effective right on every repository (or on those named), as the forge says it. Only reads. */
export async function readTeamRights(forge: TeamForge, org: string, opts: { team?: string; user?: string; repositories?: string[]; list?: boolean } = {}):
Promise<{ organisation: string; teams: TeamRights[]; user?: UserRights }> {
if (opts.user && !(await forge.accountExists(opts.user))) throw new Error(`no forge user ${opts.user}`);
const all = await forge.teams(org);
const named = opts.team ? all.filter((t) => t.name === opts.team) : all;
if (opts.team && !named.length) throw new Error(`no team "${opts.team}" in ${org}: it holds ${all.map((t) => t.name).join(", ") || "none"}`);
+6
View File
@@ -42,6 +42,8 @@ const server = createServer((req, res) => {
if (path === "/orgs/novox/teams") return send(200, teams);
const m = path.match(/^\/teams\/(\d+)\/members$/);
if (m) return send(200, (members[m[1]] ?? []).map(user));
const u = path.match(/^\/users\/([^/]+)$/);
if (u) return send(u[1] === "nobody" ? 404 : 200, u[1] === "nobody" ? { message: "not found" } : user(u[1]));
if (path === "/repos/search") return send(200, { ok: true, data: repos });
const r = path.match(/^\/repos\/([^/]+)\/([^/]+)$/);
if (r) {
@@ -94,3 +96,7 @@ test("the tools' own token is refused the teams, which is why the administrator'
const res = await fetch(`${url}/api/v1/orgs/novox/teams`, { headers: { Authorization: "token tools-token" } });
assert.equal(res.status, 403);
});
test("a user the forge does not know is refused, not answered as writing nowhere", async () => {
await assert.rejects(run({ user: "nobody" }), /no forge user nobody/);
});