ITHC: enforce representation blob proxy ownership and admin access
This commit is contained in:
@@ -2,10 +2,7 @@
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { bytesToSize } from "../../../components/utils";
|
||||
import {
|
||||
deleteAwaitingSubmissionsFromBlob,
|
||||
getRepsFromBlobProxy
|
||||
} from "../../../actions/services/documentService";
|
||||
import { deleteAwaitingSubmissionsFromBlob } from "../../../actions/services/documentService";
|
||||
import { sendRepCompleteMessage } from "../../../actions/services/portalService";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
|
||||
@@ -42,7 +39,19 @@ export default function StorageTab({ data, repCompleteCount }) {
|
||||
return data;
|
||||
})
|
||||
.then(() => {
|
||||
getRepsFromBlobProxy(containerID)
|
||||
fetch("/api/admin/getrepsblobproxy", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ container: containerID })
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
"Unable to refresh representations"
|
||||
);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
setMyRepresentations(data);
|
||||
setMyRepresentationsDetails(data);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
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 {
|
||||
hasTrustedSameOrigin,
|
||||
readAdminSessionCookie,
|
||||
setAdminAuthNoStore,
|
||||
validateAdminSession
|
||||
} from "../../../lib/admin/adminAuth";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const BASE_URL = process.env.API_ROOT || `http://localhost:${port}`;
|
||||
|
||||
const forbidden = (res) =>
|
||||
respondError(res, {
|
||||
status: 403,
|
||||
code: "FORBIDDEN",
|
||||
message: "Request not permitted"
|
||||
});
|
||||
|
||||
const isValidContainer = (value) =>
|
||||
typeof value === "string" &&
|
||||
value.length >= 3 &&
|
||||
value.length <= 63 &&
|
||||
/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value);
|
||||
|
||||
export default async function handler(req, res) {
|
||||
setAdminAuthNoStore(res);
|
||||
res.setHeader("Allow", "POST");
|
||||
|
||||
if (req.method !== "POST") {
|
||||
return respondError(res, {
|
||||
status: 405,
|
||||
code: "METHOD_NOT_ALLOWED",
|
||||
message: "Method not allowed"
|
||||
});
|
||||
}
|
||||
|
||||
const adminSession = readAdminSessionCookie(req);
|
||||
if (
|
||||
!validateAdminSession(adminSession) ||
|
||||
!hasTrustedSameOrigin(req)
|
||||
) {
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
const { container } = req.body ?? {};
|
||||
if (!isValidContainer(container)) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_REQUEST",
|
||||
message: "Invalid request"
|
||||
});
|
||||
}
|
||||
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"/api/file/getrepsblob?container=" + encodeURIComponent(container);
|
||||
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
BASE_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_REPS_BLOB_PROXY_FAILED",
|
||||
message: "Failed to fetch representation blobs"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,37 @@ import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import axios from "axios";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
import { getSession } from "next-auth/react";
|
||||
|
||||
const BASE_URL = process.env.API_ROOT || `http://localhost:${port}`;
|
||||
|
||||
const hasValue = (value) =>
|
||||
typeof value === "string" && value.trim().length > 0;
|
||||
const forbidden = (res) =>
|
||||
respondError(res, {
|
||||
status: 403,
|
||||
code: "FORBIDDEN",
|
||||
message: "Request not permitted"
|
||||
});
|
||||
|
||||
const isValidContainer = (value) =>
|
||||
typeof value === "string" &&
|
||||
value.length >= 3 &&
|
||||
value.length <= 63 &&
|
||||
/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value);
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
res.setHeader("Allow", "GET");
|
||||
|
||||
if (req.method !== "GET") {
|
||||
return respondError(res, {
|
||||
status: 405,
|
||||
code: "METHOD_NOT_ALLOWED",
|
||||
message: "Method not allowed"
|
||||
});
|
||||
}
|
||||
|
||||
const containerName = req.query.container;
|
||||
|
||||
if (!hasValue(containerName)) {
|
||||
if (typeof containerName === "undefined" || containerName === "") {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
@@ -21,6 +42,23 @@ export default async function ApiProxy(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
if (!isValidContainer(containerName)) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_REQUEST",
|
||||
message: "Invalid request"
|
||||
});
|
||||
}
|
||||
|
||||
const session = await getSession({ req });
|
||||
if (
|
||||
typeof session?.user?.id !== "string" ||
|
||||
session.user.id.length === 0 ||
|
||||
session.user.id !== containerName
|
||||
) {
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
const token = await getToken();
|
||||
|
||||
const queryUrl =
|
||||
|
||||
@@ -908,6 +908,91 @@ test("getrepsblob handler success returns representations payload", async () =>
|
||||
});
|
||||
});
|
||||
|
||||
test("getrepsblob handler rejects missing HMAC before Azure access", async () => {
|
||||
let azureCalls = 0;
|
||||
const mod = loadModule("pages/api/file/getrepsblob.js", {
|
||||
nextConnect: createNextConnectMock(),
|
||||
middleware: () => {},
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
consoleLogger: () => {},
|
||||
getRepsBlobs: async () => {
|
||||
azureCalls += 1;
|
||||
return [];
|
||||
},
|
||||
downloadAllRepsFiles: async () => {
|
||||
azureCalls += 1;
|
||||
return {};
|
||||
}
|
||||
});
|
||||
const res = createRes();
|
||||
|
||||
await mod.default.handler({ query: { container: "c1" } }, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "MISSING_REQUIRED_QUERY");
|
||||
assert.strictEqual(azureCalls, 0);
|
||||
});
|
||||
|
||||
test("getrepsblob handler rejects invalid HMAC before Azure access", async () => {
|
||||
let azureCalls = 0;
|
||||
const mod = loadModule("pages/api/file/getrepsblob.js", {
|
||||
nextConnect: createNextConnectMock(),
|
||||
middleware: () => {},
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
consoleLogger: () => {},
|
||||
getRepsBlobs: async () => {
|
||||
azureCalls += 1;
|
||||
return [];
|
||||
},
|
||||
downloadAllRepsFiles: async () => {
|
||||
azureCalls += 1;
|
||||
return {};
|
||||
}
|
||||
});
|
||||
const res = createRes();
|
||||
|
||||
await mod.default.handler(
|
||||
{ query: { container: "c1", hash: "wrong" } },
|
||||
res
|
||||
);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH");
|
||||
assert.strictEqual(azureCalls, 0);
|
||||
});
|
||||
|
||||
test("general hash endpoint does not authorize getrepsblob signing", async () => {
|
||||
let hashCalls = 0;
|
||||
const mod = loadModule("pages/api/endpoint/gethash_api.js", {
|
||||
nextConnect: createNextConnectMock(),
|
||||
getSession: async () => ({ user: { id: "user-container" } }),
|
||||
hashAPIPath: () => {
|
||||
hashCalls += 1;
|
||||
return "&hash=unexpected";
|
||||
},
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock
|
||||
});
|
||||
const res = createRes();
|
||||
|
||||
await mod.default.handler(
|
||||
{
|
||||
query: {
|
||||
path: "/api/file/getrepsblob?container=user-container"
|
||||
}
|
||||
},
|
||||
res
|
||||
);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH_PATH");
|
||||
assert.strictEqual(hashCalls, 0);
|
||||
});
|
||||
|
||||
test("getrepsblob handler dependency failure returns GET_REPS_BLOB_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/file/getrepsblob.js", {
|
||||
nextConnect: createNextConnectMock(),
|
||||
@@ -1138,30 +1223,324 @@ test("getbloblistproxy dependency failure returns GET_BLOB_LIST_PROXY_FAILED", a
|
||||
);
|
||||
});
|
||||
|
||||
test("getrepsblobproxy success returns proxied payload", async () => {
|
||||
const loadOrdinaryRepsProxy = (overrides = {}) => {
|
||||
const calls = { token: 0, hash: [], axios: [] };
|
||||
const payload = { "@odata.count": 1, value: [{ id: "r1" }] };
|
||||
const mod = loadModule("pages/api/file/getrepsblobproxy.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "tok" }),
|
||||
azureHeaders: () => ({ headers: {} }),
|
||||
hashAPIPath: () => "&hash=abc",
|
||||
getSession: async () => ({ user: { id: "user-container" } }),
|
||||
getToken: async () => {
|
||||
calls.token += 1;
|
||||
return { access_token: "tok" };
|
||||
},
|
||||
azureHeaders: () => ({ headers: { Authorization: "Bearer tok" } }),
|
||||
hashAPIPath: (queryPath) => {
|
||||
calls.hash.push(queryPath);
|
||||
return "&hash=abc";
|
||||
},
|
||||
consoleLogger: () => {},
|
||||
process: { env: { API_ROOT: "http://example.com" } },
|
||||
axios: {
|
||||
get: async () => ({ data: { value: [{ id: "r1" }] } })
|
||||
}
|
||||
get: async (...args) => {
|
||||
calls.axios.push(args);
|
||||
return { data: payload };
|
||||
}
|
||||
},
|
||||
...overrides
|
||||
});
|
||||
|
||||
const req = {
|
||||
query: { container: "c1" }
|
||||
};
|
||||
return { mod, calls, payload };
|
||||
};
|
||||
|
||||
const ordinaryRepsRequest = (overrides = {}) => ({
|
||||
method: "GET",
|
||||
query: { container: "user-container" },
|
||||
headers: {},
|
||||
...overrides
|
||||
});
|
||||
|
||||
const assertNoProxyWork = (calls) => {
|
||||
assert.strictEqual(calls.token, 0);
|
||||
assert.strictEqual(calls.hash.length, 0);
|
||||
assert.strictEqual(calls.axios.length, 0);
|
||||
};
|
||||
|
||||
for (const sessionCase of [
|
||||
{ name: "missing session", session: null },
|
||||
{ name: "session without user id", session: { user: {} } },
|
||||
{ name: "session with empty user id", session: { user: { id: "" } } },
|
||||
{ name: "session with non-string user id", session: { user: { id: 123 } } }
|
||||
]) {
|
||||
test(`getrepsblobproxy rejects ${sessionCase.name} before proxy work`, async () => {
|
||||
const loaded = loadOrdinaryRepsProxy({
|
||||
getSession: async () => sessionCase.session
|
||||
});
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(ordinaryRepsRequest(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "FORBIDDEN");
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
}
|
||||
|
||||
test("getrepsblobproxy rejects cross-container access before proxy work", async () => {
|
||||
const loaded = loadOrdinaryRepsProxy({
|
||||
getSession: async () => ({ user: { id: "different-container" } })
|
||||
});
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
await loaded.mod.default(ordinaryRepsRequest(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "FORBIDDEN");
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
|
||||
test("administrative cookie alone cannot authorize getrepsblobproxy", async () => {
|
||||
const loaded = loadOrdinaryRepsProxy({ getSession: async () => null });
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(
|
||||
ordinaryRepsRequest({
|
||||
headers: { cookie: "pedw_admin_session=valid-admin-token" }
|
||||
}),
|
||||
res
|
||||
);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
|
||||
test("getrepsblobproxy rejects unsupported methods before proxy work", async () => {
|
||||
const loaded = loadOrdinaryRepsProxy();
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(ordinaryRepsRequest({ method: "POST" }), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 405);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "METHOD_NOT_ALLOWED");
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
|
||||
for (const containerCase of [
|
||||
{ name: "missing container", query: {} },
|
||||
{ name: "empty container", query: { container: "" } },
|
||||
{ name: "null container", query: { container: null } },
|
||||
{ name: "non-string container", query: { container: ["user-container"] } },
|
||||
{ name: "malformed container", query: { container: "Invalid_Container" } }
|
||||
]) {
|
||||
test(`getrepsblobproxy rejects ${containerCase.name} before proxy work`, async () => {
|
||||
const loaded = loadOrdinaryRepsProxy();
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(
|
||||
ordinaryRepsRequest({ query: containerCase.query }),
|
||||
res
|
||||
);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
}
|
||||
|
||||
test("getrepsblobproxy preserves signed path and response for owning user", async () => {
|
||||
const loaded = loadOrdinaryRepsProxy();
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(ordinaryRepsRequest(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 200);
|
||||
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
|
||||
value: [{ id: "r1" }]
|
||||
assert.strictEqual(loaded.calls.token, 1);
|
||||
assert.deepStrictEqual(loaded.calls.hash, [
|
||||
"/api/file/getrepsblob?container=user-container"
|
||||
]);
|
||||
assert.strictEqual(
|
||||
loaded.calls.axios[0][0],
|
||||
"http://example.com/api/file/getrepsblob?container=user-container&hash=abc"
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
JSON.parse(JSON.stringify(res.state.jsonBody)),
|
||||
loaded.payload
|
||||
);
|
||||
});
|
||||
|
||||
const loadAdminRepsProxy = (overrides = {}) => {
|
||||
const calls = { token: 0, hash: [], axios: [] };
|
||||
const payload = { "@odata.count": 1, value: [{ id: "admin-r1" }] };
|
||||
const mod = loadModule("pages/api/admin/getrepsblobproxy.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
readAdminSessionCookie: () => "admin-token",
|
||||
validateAdminSession: () => true,
|
||||
hasTrustedSameOrigin: () => true,
|
||||
setAdminAuthNoStore: () => {},
|
||||
getToken: async () => {
|
||||
calls.token += 1;
|
||||
return { access_token: "tok" };
|
||||
},
|
||||
azureHeaders: () => ({ headers: { Authorization: "Bearer tok" } }),
|
||||
hashAPIPath: (queryPath) => {
|
||||
calls.hash.push(queryPath);
|
||||
return "&hash=abc";
|
||||
},
|
||||
consoleLogger: () => {},
|
||||
process: { env: { API_ROOT: "http://example.com" } },
|
||||
axios: {
|
||||
get: async (...args) => {
|
||||
calls.axios.push(args);
|
||||
return { data: payload };
|
||||
}
|
||||
},
|
||||
...overrides
|
||||
});
|
||||
|
||||
return { mod, calls, payload };
|
||||
};
|
||||
|
||||
const adminRepsRequest = (overrides = {}) => ({
|
||||
method: "POST",
|
||||
headers: { origin: "https://portal.example.test" },
|
||||
body: { container: "selected-user-container" },
|
||||
...overrides
|
||||
});
|
||||
|
||||
for (const adminSessionCase of [
|
||||
{
|
||||
name: "missing administrative session",
|
||||
overrides: {
|
||||
readAdminSessionCookie: () => null,
|
||||
validateAdminSession: () => false
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "invalid administrative session",
|
||||
overrides: { validateAdminSession: () => false }
|
||||
},
|
||||
{
|
||||
name: "expired administrative session",
|
||||
overrides: { validateAdminSession: () => false }
|
||||
}
|
||||
]) {
|
||||
test(`admin getrepsblobproxy rejects ${adminSessionCase.name} before proxy work`, async () => {
|
||||
const loaded = loadAdminRepsProxy(adminSessionCase.overrides);
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(adminRepsRequest(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "FORBIDDEN");
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
}
|
||||
|
||||
for (const originCase of ["missing Origin", "cross-origin request"]) {
|
||||
test(`admin getrepsblobproxy rejects ${originCase} before proxy work`, async () => {
|
||||
const loaded = loadAdminRepsProxy({ hasTrustedSameOrigin: () => false });
|
||||
const req = adminRepsRequest();
|
||||
if (originCase === "missing Origin") delete req.headers.origin;
|
||||
if (originCase === "cross-origin request") {
|
||||
req.headers.origin = "https://attacker.example";
|
||||
}
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
}
|
||||
|
||||
for (const containerCase of [
|
||||
{ name: "missing container", body: {} },
|
||||
{ name: "malformed container", body: { container: "Invalid_Container" } }
|
||||
]) {
|
||||
test(`admin getrepsblobproxy rejects ${containerCase.name} before proxy work`, async () => {
|
||||
const loaded = loadAdminRepsProxy();
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(
|
||||
adminRepsRequest({ body: containerCase.body }),
|
||||
res
|
||||
);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "INVALID_REQUEST");
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
}
|
||||
|
||||
test("admin getrepsblobproxy rejects unsupported methods before proxy work", async () => {
|
||||
const loaded = loadAdminRepsProxy();
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(adminRepsRequest({ method: "GET" }), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 405);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "METHOD_NOT_ALLOWED");
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
|
||||
test("ordinary NextAuth session alone cannot authorize admin getrepsblobproxy", async () => {
|
||||
const loaded = loadAdminRepsProxy({
|
||||
readAdminSessionCookie: () => null,
|
||||
validateAdminSession: () => false,
|
||||
getSession: async () => ({ user: { id: "selected-user-container" } })
|
||||
});
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(adminRepsRequest(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
assertNoProxyWork(loaded.calls);
|
||||
});
|
||||
|
||||
test("admin getrepsblobproxy preserves cross-container signed path and response", async () => {
|
||||
const loaded = loadAdminRepsProxy();
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(adminRepsRequest(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 200);
|
||||
assert.strictEqual(loaded.calls.token, 1);
|
||||
assert.deepStrictEqual(loaded.calls.hash, [
|
||||
"/api/file/getrepsblob?container=selected-user-container"
|
||||
]);
|
||||
assert.strictEqual(
|
||||
loaded.calls.axios[0][0],
|
||||
"http://example.com/api/file/getrepsblob?container=selected-user-container&hash=abc"
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
JSON.parse(JSON.stringify(res.state.jsonBody)),
|
||||
loaded.payload
|
||||
);
|
||||
});
|
||||
|
||||
test("admin storage refresh uses dedicated POST proxy while ordinary callers remain unchanged", () => {
|
||||
const rootDir = path.resolve(__dirname, "..", "..");
|
||||
const adminSource = fs.readFileSync(
|
||||
path.join(rootDir, "components/admin/tabs/storage.js"),
|
||||
"utf8"
|
||||
);
|
||||
const topThreeSource = fs.readFileSync(
|
||||
path.join(rootDir, "components/myportal/topthree_reps.js"),
|
||||
"utf8"
|
||||
);
|
||||
const viewAllSource = fs.readFileSync(
|
||||
path.join(rootDir, "components/myportal/viewall.js"),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
assert.match(adminSource, /fetch\("\/api\/admin\/getrepsblobproxy"/);
|
||||
assert.match(
|
||||
adminSource,
|
||||
/fetch\("\/api\/admin\/getrepsblobproxy"[\s\S]*method:\s*"POST"[\s\S]*JSON\.stringify\(\{ container: containerID \}\)/
|
||||
);
|
||||
assert.doesNotMatch(adminSource, /getRepsFromBlobProxy/);
|
||||
assert.match(topThreeSource, /getRepsFromBlobProxy\(containerID\)/);
|
||||
assert.match(viewAllSource, /getRepsFromBlobProxy\(containerID\)/);
|
||||
});
|
||||
|
||||
test("getawaitingsubmissionfromblobproxy dependency failure returns GET_AWAITING_SUBMISSION_PROXY_FAILED", async () => {
|
||||
|
||||
Reference in New Issue
Block a user