fix: enforce representation blob ownership and secure SSR access
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { BASE_URL } from "../../../actions/core/env";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import axios from "axios";
|
||||
import {
|
||||
ADMIN_SESSION_COOKIE,
|
||||
hasTrustedSameOrigin,
|
||||
readAdminSessionCookie,
|
||||
setAdminAuthNoStore,
|
||||
@@ -11,7 +13,16 @@ import {
|
||||
} from "../../../lib/admin/adminAuth";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const BASE_URL = process.env.API_ROOT || `http://localhost:${port}`;
|
||||
const isTrustedInternalDestination = () => {
|
||||
try {
|
||||
return (
|
||||
new URL(BASE_URL).origin ===
|
||||
new URL(process.env.NEXTAUTH_URL).origin
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const forbidden = (res) =>
|
||||
respondError(res, {
|
||||
@@ -39,10 +50,7 @@ export default async function handler(req, res) {
|
||||
}
|
||||
|
||||
const adminSession = readAdminSessionCookie(req);
|
||||
if (
|
||||
!validateAdminSession(adminSession) ||
|
||||
!hasTrustedSameOrigin(req)
|
||||
) {
|
||||
if (!validateAdminSession(adminSession) || !hasTrustedSameOrigin(req)) {
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
@@ -55,14 +63,27 @@ export default async function handler(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
if (!isTrustedInternalDestination()) {
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"/api/file/getrepsblob?container=" + encodeURIComponent(container);
|
||||
|
||||
try {
|
||||
const config = azureHeaders(token.access_token);
|
||||
|
||||
const { data } = await axios.get(
|
||||
BASE_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
{
|
||||
...config,
|
||||
headers: {
|
||||
...config.headers,
|
||||
Cookie: `${ADMIN_SESSION_COOKIE}=${encodeURIComponent(adminSession)}`
|
||||
},
|
||||
maxRedirects: 0
|
||||
}
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
@@ -73,4 +94,4 @@ export default async function handler(req, res) {
|
||||
message: "Failed to fetch representation blobs"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,23 @@ import middleware from "../middleware/middleware";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { authOptions } from "../auth/[...nextauth]";
|
||||
import {
|
||||
readAdminSessionCookie,
|
||||
validateAdminSession
|
||||
} from "../../../lib/admin/adminAuth";
|
||||
|
||||
const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
const forbidden = (res) =>
|
||||
respondError(res, {
|
||||
status: 403,
|
||||
code: "FORBIDDEN",
|
||||
message: "Request not permitted"
|
||||
});
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
const containerName = req.query.container;
|
||||
const checkHash = req.query.hash;
|
||||
@@ -59,6 +72,17 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const session = await getServerSession(req, res, authOptions(req, res));
|
||||
const isPortalOwner =
|
||||
typeof session?.user?.id === "string" &&
|
||||
session.user.id.length > 0 &&
|
||||
session.user.id === containerName;
|
||||
const isAdministrator = validateAdminSession(readAdminSessionCookie(req));
|
||||
|
||||
if (!isPortalOwner && !isAdministrator) {
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/getrepsblob?container=" + containerName,
|
||||
"/api/file/getrepsblob?container=" + encodeURIComponent(containerName)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { BASE_URL } from "../../../actions/core/env";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
@@ -7,7 +8,16 @@ import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { authOptions } from "../auth/[...nextauth]";
|
||||
|
||||
const BASE_URL = process.env.API_ROOT || `http://localhost:${port}`;
|
||||
const isTrustedInternalDestination = () => {
|
||||
try {
|
||||
return (
|
||||
new URL(BASE_URL).origin ===
|
||||
new URL(process.env.NEXTAUTH_URL).origin
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const forbidden = (res) =>
|
||||
respondError(res, {
|
||||
@@ -60,15 +70,28 @@ export default async function ApiProxy(req, res) {
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
if (!isTrustedInternalDestination()) {
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
const token = await getToken();
|
||||
|
||||
const queryUrl =
|
||||
"/api/file/getrepsblob?container=" + encodeURIComponent(containerName);
|
||||
|
||||
try {
|
||||
const config = azureHeaders(token.access_token);
|
||||
|
||||
const { data } = await axios.get(
|
||||
BASE_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
{
|
||||
...config,
|
||||
headers: {
|
||||
...config.headers,
|
||||
Cookie: req.headers.cookie || ""
|
||||
},
|
||||
maxRedirects: 0
|
||||
}
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user