mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 10:23:14 +00:00
fix: conditionally disable reqs to the server in prod (#456)
* fix: conditionally disable reqs to the server in prod * cr
This commit is contained in:
parent
9a4b63b798
commit
05bc1b3559
1 changed files with 188 additions and 184 deletions
|
|
@ -1,24 +1,24 @@
|
|||
import { Auth, HTTPException } from "@langchain/langgraph-sdk/auth";
|
||||
// import {
|
||||
// verifyGithubUser,
|
||||
// GithubUser,
|
||||
// verifyGithubUserId,
|
||||
// } from "@open-swe/shared/github/verify-user";
|
||||
// import {
|
||||
// API_KEY_REQUIRED_MESSAGE,
|
||||
// GITHUB_INSTALLATION_NAME,
|
||||
// GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
// GITHUB_TOKEN_COOKIE,
|
||||
// GITHUB_USER_ID_HEADER,
|
||||
// GITHUB_USER_LOGIN_HEADER,
|
||||
// } from "@open-swe/shared/constants";
|
||||
// import { decryptSecret } from "@open-swe/shared/crypto";
|
||||
// import { verifyGitHubWebhookOrThrow } from "./github.js";
|
||||
import {
|
||||
verifyGithubUser,
|
||||
GithubUser,
|
||||
verifyGithubUserId,
|
||||
} from "@open-swe/shared/github/verify-user";
|
||||
import {
|
||||
API_KEY_REQUIRED_MESSAGE,
|
||||
GITHUB_INSTALLATION_NAME,
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_TOKEN_COOKIE,
|
||||
GITHUB_USER_ID_HEADER,
|
||||
GITHUB_USER_LOGIN_HEADER,
|
||||
} from "@open-swe/shared/constants";
|
||||
import { decryptSecret } from "@open-swe/shared/crypto";
|
||||
import { verifyGitHubWebhookOrThrow } from "./github.js";
|
||||
import { createWithOwnerMetadata, createOwnerFilter } from "./utils.js";
|
||||
// import { LANGGRAPH_USER_PERMISSIONS } from "../constants.js";
|
||||
// import { getGitHubPatFromRequest } from "../utils/github-pat.js";
|
||||
// import { isAllowedUser } from "../utils/github/allowed-users.js";
|
||||
// import { validate } from "uuid";
|
||||
import { LANGGRAPH_USER_PERMISSIONS } from "../constants.js";
|
||||
import { getGitHubPatFromRequest } from "../utils/github-pat.js";
|
||||
import { isAllowedUser } from "../utils/github/allowed-users.js";
|
||||
import { validate } from "uuid";
|
||||
|
||||
// TODO: Export from LangGraph SDK
|
||||
export interface BaseAuthReturn {
|
||||
|
|
@ -34,189 +34,193 @@ interface AuthenticateReturn extends BaseAuthReturn {
|
|||
};
|
||||
}
|
||||
|
||||
// function apiKeysInRequestBody(
|
||||
// bodyStr: string | Record<string, unknown>,
|
||||
// ): boolean {
|
||||
// try {
|
||||
// const body = typeof bodyStr === "string" ? JSON.parse(bodyStr) : bodyStr;
|
||||
// if (
|
||||
// body.config?.configurable &&
|
||||
// ("anthropicApiKey" in body.config.configurable.apiKeys ||
|
||||
// "openaiApiKey" in body.config.configurable.apiKeys ||
|
||||
// "googleApiKey" in body.config.configurable.apiKeys)
|
||||
// ) {
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
// } catch {
|
||||
// // no-op
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
function apiKeysInRequestBody(
|
||||
bodyStr: string | Record<string, unknown>,
|
||||
): boolean {
|
||||
try {
|
||||
const body = typeof bodyStr === "string" ? JSON.parse(bodyStr) : bodyStr;
|
||||
if (
|
||||
body.config?.configurable &&
|
||||
("anthropicApiKey" in body.config.configurable.apiKeys ||
|
||||
"openaiApiKey" in body.config.configurable.apiKeys ||
|
||||
"googleApiKey" in body.config.configurable.apiKeys)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
// no-op
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// function isRunReq(reqUrl: string): boolean {
|
||||
// try {
|
||||
// const url = new URL(reqUrl);
|
||||
// const pathnameParts = url.pathname.split("/");
|
||||
// const isCreateAndWait = !!(
|
||||
// pathnameParts[1] === "threads" &&
|
||||
// validate(pathnameParts[2]) &&
|
||||
// pathnameParts[3] === "runs" &&
|
||||
// pathnameParts[4] === "wait" &&
|
||||
// pathnameParts.length === 5
|
||||
// );
|
||||
// const isCreateBackground = !!(
|
||||
// pathnameParts[1] === "threads" &&
|
||||
// validate(pathnameParts[2]) &&
|
||||
// pathnameParts[3] === "runs" &&
|
||||
// pathnameParts.length === 4
|
||||
// );
|
||||
// const isCreateStream = !!(
|
||||
// pathnameParts[1] === "threads" &&
|
||||
// validate(pathnameParts[2]) &&
|
||||
// pathnameParts[3] === "runs" &&
|
||||
// pathnameParts[4] === "stream" &&
|
||||
// pathnameParts.length === 5
|
||||
// );
|
||||
function isRunReq(reqUrl: string): boolean {
|
||||
try {
|
||||
const url = new URL(reqUrl);
|
||||
const pathnameParts = url.pathname.split("/");
|
||||
const isCreateAndWait = !!(
|
||||
pathnameParts[1] === "threads" &&
|
||||
validate(pathnameParts[2]) &&
|
||||
pathnameParts[3] === "runs" &&
|
||||
pathnameParts[4] === "wait" &&
|
||||
pathnameParts.length === 5
|
||||
);
|
||||
const isCreateBackground = !!(
|
||||
pathnameParts[1] === "threads" &&
|
||||
validate(pathnameParts[2]) &&
|
||||
pathnameParts[3] === "runs" &&
|
||||
pathnameParts.length === 4
|
||||
);
|
||||
const isCreateStream = !!(
|
||||
pathnameParts[1] === "threads" &&
|
||||
validate(pathnameParts[2]) &&
|
||||
pathnameParts[3] === "runs" &&
|
||||
pathnameParts[4] === "stream" &&
|
||||
pathnameParts.length === 5
|
||||
);
|
||||
|
||||
// return !!isCreateAndWait || !!isCreateBackground || !!isCreateStream;
|
||||
// } catch {
|
||||
// // no-op
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
return !!isCreateAndWait || !!isCreateBackground || !!isCreateStream;
|
||||
} catch {
|
||||
// no-op
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const auth = new Auth()
|
||||
.authenticate<AuthenticateReturn>(async (_request: Request) => {
|
||||
return new HTTPException(504, {
|
||||
message: "Open SWE temporarily disabled.",
|
||||
}) as any;
|
||||
.authenticate<AuthenticateReturn>(async (request: Request) => {
|
||||
const isProdEnv = process.env.NODE_ENV === "production";
|
||||
// Disable all requests to the server in prod for now.
|
||||
if (!isProdEnv) {
|
||||
return new HTTPException(504, {
|
||||
message: "Open SWE temporarily disabled.",
|
||||
}) as any;
|
||||
}
|
||||
|
||||
// if (request.method === "OPTIONS") {
|
||||
// return {
|
||||
// identity: "anonymous",
|
||||
// permissions: [],
|
||||
// is_authenticated: false,
|
||||
// display_name: "CORS Preflight",
|
||||
// metadata: {
|
||||
// installation_name: "n/a",
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
if (request.method === "OPTIONS") {
|
||||
return {
|
||||
identity: "anonymous",
|
||||
permissions: [],
|
||||
is_authenticated: false,
|
||||
display_name: "CORS Preflight",
|
||||
metadata: {
|
||||
installation_name: "n/a",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// const isProd = process.env.NODE_ENV === "production";
|
||||
const isProd = process.env.NODE_ENV === "production";
|
||||
|
||||
// const ghSecretHashHeader = request.headers.get("X-Hub-Signature-256");
|
||||
// if (ghSecretHashHeader) {
|
||||
// // This will either return a valid user, or throw an error
|
||||
// return await verifyGitHubWebhookOrThrow(request);
|
||||
// }
|
||||
const ghSecretHashHeader = request.headers.get("X-Hub-Signature-256");
|
||||
if (ghSecretHashHeader) {
|
||||
// This will either return a valid user, or throw an error
|
||||
return await verifyGitHubWebhookOrThrow(request);
|
||||
}
|
||||
|
||||
// const encryptionKey = process.env.SECRETS_ENCRYPTION_KEY;
|
||||
// if (!encryptionKey) {
|
||||
// throw new Error("Missing SECRETS_ENCRYPTION_KEY environment variable.");
|
||||
// }
|
||||
const encryptionKey = process.env.SECRETS_ENCRYPTION_KEY;
|
||||
if (!encryptionKey) {
|
||||
throw new Error("Missing SECRETS_ENCRYPTION_KEY environment variable.");
|
||||
}
|
||||
|
||||
// // Check for GitHub PAT authentication (simpler mode for evals, etc.)
|
||||
// const githubPat = getGitHubPatFromRequest(request, encryptionKey);
|
||||
// if (githubPat && !isProd) {
|
||||
// const user = await verifyGithubUser(githubPat);
|
||||
// if (!user) {
|
||||
// throw new HTTPException(401, {
|
||||
// message: "Invalid GitHub PAT",
|
||||
// });
|
||||
// }
|
||||
// Check for GitHub PAT authentication (simpler mode for evals, etc.)
|
||||
const githubPat = getGitHubPatFromRequest(request, encryptionKey);
|
||||
if (githubPat && !isProd) {
|
||||
const user = await verifyGithubUser(githubPat);
|
||||
if (!user) {
|
||||
throw new HTTPException(401, {
|
||||
message: "Invalid GitHub PAT",
|
||||
});
|
||||
}
|
||||
|
||||
// return {
|
||||
// identity: user.id.toString(),
|
||||
// is_authenticated: true,
|
||||
// display_name: user.login,
|
||||
// metadata: {
|
||||
// installation_name: "pat-auth",
|
||||
// },
|
||||
// permissions: LANGGRAPH_USER_PERMISSIONS,
|
||||
// };
|
||||
// }
|
||||
return {
|
||||
identity: user.id.toString(),
|
||||
is_authenticated: true,
|
||||
display_name: user.login,
|
||||
metadata: {
|
||||
installation_name: "pat-auth",
|
||||
},
|
||||
permissions: LANGGRAPH_USER_PERMISSIONS,
|
||||
};
|
||||
}
|
||||
|
||||
// // GitHub App authentication mode (existing logic)
|
||||
// const installationNameHeader = request.headers.get(
|
||||
// GITHUB_INSTALLATION_NAME,
|
||||
// );
|
||||
// if (!installationNameHeader) {
|
||||
// throw new HTTPException(401, {
|
||||
// message: "GitHub installation name header missing",
|
||||
// });
|
||||
// }
|
||||
// GitHub App authentication mode (existing logic)
|
||||
const installationNameHeader = request.headers.get(
|
||||
GITHUB_INSTALLATION_NAME,
|
||||
);
|
||||
if (!installationNameHeader) {
|
||||
throw new HTTPException(401, {
|
||||
message: "GitHub installation name header missing",
|
||||
});
|
||||
}
|
||||
|
||||
// // We don't do anything with this token right now, but still confirm it
|
||||
// // exists as it will cause issues later on if it's not present.
|
||||
// const encryptedInstallationToken = request.headers.get(
|
||||
// GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
// );
|
||||
// if (!encryptedInstallationToken) {
|
||||
// throw new HTTPException(401, {
|
||||
// message: "GitHub installation token header missing",
|
||||
// });
|
||||
// }
|
||||
// We don't do anything with this token right now, but still confirm it
|
||||
// exists as it will cause issues later on if it's not present.
|
||||
const encryptedInstallationToken = request.headers.get(
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
);
|
||||
if (!encryptedInstallationToken) {
|
||||
throw new HTTPException(401, {
|
||||
message: "GitHub installation token header missing",
|
||||
});
|
||||
}
|
||||
|
||||
// const encryptedAccessToken = request.headers.get(GITHUB_TOKEN_COOKIE);
|
||||
// const decryptedAccessToken = encryptedAccessToken
|
||||
// ? decryptSecret(encryptedAccessToken, encryptionKey)
|
||||
// : undefined;
|
||||
// const decryptedInstallationToken = decryptSecret(
|
||||
// encryptedInstallationToken,
|
||||
// encryptionKey,
|
||||
// );
|
||||
const encryptedAccessToken = request.headers.get(GITHUB_TOKEN_COOKIE);
|
||||
const decryptedAccessToken = encryptedAccessToken
|
||||
? decryptSecret(encryptedAccessToken, encryptionKey)
|
||||
: undefined;
|
||||
const decryptedInstallationToken = decryptSecret(
|
||||
encryptedInstallationToken,
|
||||
encryptionKey,
|
||||
);
|
||||
|
||||
// let user: GithubUser | undefined;
|
||||
let user: GithubUser | undefined;
|
||||
|
||||
// if (!decryptedAccessToken) {
|
||||
// // If there isn't a user access token, check to see if the user info is in headers.
|
||||
// // This would indicate a bot created the request.
|
||||
// const userIdHeader = request.headers.get(GITHUB_USER_ID_HEADER);
|
||||
// const userLoginHeader = request.headers.get(GITHUB_USER_LOGIN_HEADER);
|
||||
// if (!userIdHeader || !userLoginHeader) {
|
||||
// throw new HTTPException(401, {
|
||||
// message: "Github-User-Id or Github-User-Login header missing",
|
||||
// });
|
||||
// }
|
||||
// user = await verifyGithubUserId(
|
||||
// decryptedInstallationToken,
|
||||
// Number(userIdHeader),
|
||||
// userLoginHeader,
|
||||
// );
|
||||
// } else {
|
||||
// // Ensure we decrypt the token before passing to the verification function.
|
||||
// user = await verifyGithubUser(decryptedAccessToken);
|
||||
// }
|
||||
if (!decryptedAccessToken) {
|
||||
// If there isn't a user access token, check to see if the user info is in headers.
|
||||
// This would indicate a bot created the request.
|
||||
const userIdHeader = request.headers.get(GITHUB_USER_ID_HEADER);
|
||||
const userLoginHeader = request.headers.get(GITHUB_USER_LOGIN_HEADER);
|
||||
if (!userIdHeader || !userLoginHeader) {
|
||||
throw new HTTPException(401, {
|
||||
message: "Github-User-Id or Github-User-Login header missing",
|
||||
});
|
||||
}
|
||||
user = await verifyGithubUserId(
|
||||
decryptedInstallationToken,
|
||||
Number(userIdHeader),
|
||||
userLoginHeader,
|
||||
);
|
||||
} else {
|
||||
// Ensure we decrypt the token before passing to the verification function.
|
||||
user = await verifyGithubUser(decryptedAccessToken);
|
||||
}
|
||||
|
||||
// if (!user) {
|
||||
// throw new HTTPException(401, {
|
||||
// message: "User not found",
|
||||
// });
|
||||
// }
|
||||
if (!user) {
|
||||
throw new HTTPException(401, {
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
// const reqCopy = request.clone();
|
||||
// const reqBody = await reqCopy.text();
|
||||
// if (!isAllowedUser(user.login)) {
|
||||
// if (isRunReq(request.url)) {
|
||||
// if (!apiKeysInRequestBody(reqBody)) {
|
||||
// throw new HTTPException(401, {
|
||||
// message: API_KEY_REQUIRED_MESSAGE,
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
const reqCopy = request.clone();
|
||||
const reqBody = await reqCopy.text();
|
||||
if (!isAllowedUser(user.login)) {
|
||||
if (isRunReq(request.url)) {
|
||||
if (!apiKeysInRequestBody(reqBody)) {
|
||||
throw new HTTPException(401, {
|
||||
message: API_KEY_REQUIRED_MESSAGE,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return {
|
||||
// identity: user.id.toString(),
|
||||
// is_authenticated: true,
|
||||
// display_name: user.login,
|
||||
// metadata: {
|
||||
// installation_name: installationNameHeader,
|
||||
// },
|
||||
// permissions: LANGGRAPH_USER_PERMISSIONS,
|
||||
// };
|
||||
return {
|
||||
identity: user.id.toString(),
|
||||
is_authenticated: true,
|
||||
display_name: user.login,
|
||||
metadata: {
|
||||
installation_name: installationNameHeader,
|
||||
},
|
||||
permissions: LANGGRAPH_USER_PERMISSIONS,
|
||||
};
|
||||
})
|
||||
|
||||
// THREADS: create operations with metadata
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue