Skip to content

Commit

Permalink
Fix find authorized user for GHE prebuilds
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexTugarev authored and roboquat committed Aug 1, 2022
1 parent 9438a3d commit 532960e
Showing 1 changed file with 60 additions and 32 deletions.
92 changes: 60 additions & 32 deletions components/server/ee/src/prebuilds/github-enterprise-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class GitHubEnterpriseApp {
}
if (!user) {
res.statusCode = 401;
res.send("Unauthorized: Cannot find user.");
res.send("Unauthorized: Cannot find authorized user.");
span.finish();
await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" });
return;
Expand All @@ -82,7 +82,7 @@ export class GitHubEnterpriseApp {
ctx: TraceContext,
payload: GitHubEnterprisePushPayload,
req: express.Request,
): Promise<User> {
): Promise<User | undefined> {
const span = TraceContext.startSpan("GitHubEnterpriseApp.findUser", ctx);
try {
let host = req.header("X-Github-Enterprise-Host");
Expand All @@ -95,38 +95,39 @@ export class GitHubEnterpriseApp {
if (!hostContext) {
throw new Error("Unsupported GitHub Enterprise host: " + host);
}
const { authProviderId } = hostContext.authProvider;
const authId = payload.sender.id;
const user = await this.userDB.findUserByIdentity({ authProviderId, authId });
if (!user) {
throw new Error(`No user found with identity ${authProviderId}/${authId}.`);
} else if (!!user.blocked) {
throw new Error(`Blocked user ${user.id} tried to start prebuild.`);
}
const gitpodIdentity = user.identities.find(
(i) => i.authProviderId === TokenService.GITPOD_AUTH_PROVIDER_ID,
);
if (!gitpodIdentity) {
throw new Error(`User ${user.id} has no identity for '${TokenService.GITPOD_AUTH_PROVIDER_ID}'.`);
const cloneURL = payload.repository.clone_url;
const projectOwners = await this.findProjectOwners(cloneURL);
if (!projectOwners) {
throw new Error("No project found.");
}
// Verify the webhook signature
const signature = req.header("X-Hub-Signature-256");
const body = (req as any).rawBody;
const tokenEntries = (await this.userDB.findTokensForIdentity(gitpodIdentity)).filter((tokenEntry) => {
return tokenEntry.token.scopes.includes(GitHubService.PREBUILD_TOKEN_SCOPE);
});
const signingToken = tokenEntries.find((tokenEntry) => {
const sig =
"sha256=" +
createHmac("sha256", user.id + "|" + tokenEntry.token.value)
.update(body)
.digest("hex");
return timingSafeEqual(Buffer.from(sig), Buffer.from(signature ?? ""));
});
if (!signingToken) {
throw new Error(`User ${user.id} has no token matching the payload signature.`);
for (const user of projectOwners.users) {
const gitpodIdentity = user.identities.find(
(i) => i.authProviderId === TokenService.GITPOD_AUTH_PROVIDER_ID,
);
if (!gitpodIdentity) {
continue;
}
// Verify the webhook signature
const signature = req.header("X-Hub-Signature-256");
const body = (req as any).rawBody;
const tokenEntries = (await this.userDB.findTokensForIdentity(gitpodIdentity)).filter((tokenEntry) => {
return tokenEntry.token.scopes.includes(GitHubService.PREBUILD_TOKEN_SCOPE);
});
const signatureMatched = tokenEntries.some((tokenEntry) => {
const sig =
"sha256=" +
createHmac("sha256", user.id + "|" + tokenEntry.token.value)
.update(body)
.digest("hex");
return timingSafeEqual(Buffer.from(sig), Buffer.from(signature ?? ""));
});
if (signatureMatched) {
if (!!user.blocked) {
throw new Error(`Blocked user ${user.id} tried to start prebuild.`);
}
return user;
}
}
return user;
} finally {
span.finish();
}
Expand Down Expand Up @@ -255,6 +256,33 @@ export class GitHubEnterpriseApp {
return { user: webhookInstaller };
}

protected async findProjectOwners(cloneURL: string): Promise<{ users: User[]; project: Project } | undefined> {
const project = await this.projectDB.findProjectByCloneUrl(cloneURL);
if (project) {
if (project.userId) {
const user = await this.userDB.findUserById(project.userId);
if (user) {
return { users: [user], project };
}
} else if (project.teamId) {
const users = [];
const owners = (await this.teamDB.findMembersByTeam(project.teamId || "")).filter(
(m) => m.role === "owner",
);
const hostContext = this.hostContextProvider.get(new URL(cloneURL).host);
const authProviderId = hostContext?.authProvider.authProviderId;
for (const teamMember of owners) {
const user = await this.userDB.findUserById(teamMember.userId);
if (user && user.identities.some((i) => i.authProviderId === authProviderId)) {
users.push(user);
}
}
return { users, project };
}
}
return undefined;
}

protected getBranchFromRef(ref: string): string | undefined {
const headsPrefix = "refs/heads/";
if (ref.startsWith(headsPrefix)) {
Expand Down

0 comments on commit 532960e

Please sign in to comment.