fix(security): enforce ownership on private draft PDF generation
This commit is contained in:
@@ -85,27 +85,68 @@ import { getPickLists } from "../../../actions/services/referenceDataService";
|
||||
import { planningappeals78_pdf } from "../../../components/pdftemplates/planningappeals78_pdf";
|
||||
import { other_pdf } from "../../../components/pdftemplates/other_pdf";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { authOptions } from "../auth/[...nextauth]";
|
||||
|
||||
//const ApiProxy = nextConnect();
|
||||
// ApiProxy.use(middleware);
|
||||
|
||||
// ApiProxy.post(async (req, res) => {
|
||||
|
||||
const isValidContainer = (value) =>
|
||||
typeof value === "string" &&
|
||||
value.length >= 3 &&
|
||||
value.length <= 63 &&
|
||||
/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value);
|
||||
|
||||
const isValidDraftFolder = (value) =>
|
||||
typeof value === "string" &&
|
||||
value.length <= 100 &&
|
||||
!value.includes("..") &&
|
||||
/^[A-Za-z0-9](?:[A-Za-z0-9/-]*[A-Za-z0-9])?$/.test(value);
|
||||
|
||||
const forbidden = (res) =>
|
||||
respondError(res, {
|
||||
status: 403,
|
||||
code: "FORBIDDEN",
|
||||
message: "Request not permitted"
|
||||
});
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const checkHash = req.query.hash;
|
||||
const appealType = req.query.appealType;
|
||||
const isDownload = req.query?.download === "true";
|
||||
|
||||
let appealBodyObj =
|
||||
typeof req.body === "string" ? JSON.parse(req.body) : req.body;
|
||||
let appealBodyObj;
|
||||
try {
|
||||
appealBodyObj =
|
||||
typeof req.body === "string" ? JSON.parse(req.body) : req.body;
|
||||
} catch (_error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_REQUEST",
|
||||
message: "Invalid request"
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!appealBodyObj ||
|
||||
typeof appealBodyObj !== "object" ||
|
||||
Array.isArray(appealBodyObj)
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_REQUEST",
|
||||
message: "Invalid request"
|
||||
});
|
||||
}
|
||||
|
||||
const containerID = appealBodyObj.containerID;
|
||||
const casefolderID = appealBodyObj.casefolderID;
|
||||
const locale = appealBodyObj.locale || "en";
|
||||
let checkquerypath = "/api/file/generateappealpdf";
|
||||
|
||||
if (
|
||||
typeof checkHash === "undefined" ||
|
||||
checkHash.length === 0 ||
|
||||
typeof appealType === "undefined" ||
|
||||
String(appealType).length === 0 ||
|
||||
typeof containerID === "undefined" ||
|
||||
@@ -121,6 +162,45 @@ export default async function handler(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
typeof appealType !== "string" ||
|
||||
!isValidContainer(containerID) ||
|
||||
!isValidDraftFolder(casefolderID) ||
|
||||
(typeof req.query.download !== "undefined" &&
|
||||
typeof req.query.download !== "string") ||
|
||||
[
|
||||
"container",
|
||||
"containerID",
|
||||
"casefolderID",
|
||||
"caseRef",
|
||||
"ticketnumber"
|
||||
].some((key) => Object.prototype.hasOwnProperty.call(req.query, key))
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_REQUEST",
|
||||
message: "Invalid request"
|
||||
});
|
||||
}
|
||||
|
||||
const session = await getServerSession(req, res, authOptions(req, res));
|
||||
if (
|
||||
typeof session?.user?.id !== "string" ||
|
||||
session.user.id.length === 0 ||
|
||||
session.user.id !== containerID
|
||||
) {
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
if (typeof checkHash !== "string" || checkHash.length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
message:
|
||||
"hash, appealType, containerID and casefolderID are required"
|
||||
});
|
||||
}
|
||||
|
||||
checkquerypath =
|
||||
checkquerypath +
|
||||
"?appealType=" +
|
||||
|
||||
@@ -84,16 +84,58 @@ import { writtenStatement_pdf } from "../../../components/pdftemplates/writtenSt
|
||||
import { other_pdf } from "../../../components/pdftemplates/other_pdf";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { authOptions } from "../auth/[...nextauth]";
|
||||
//const ApiProxy = nextConnect();
|
||||
// ApiProxy.use(middleware);
|
||||
|
||||
// ApiProxy.post(async (req, res) => {
|
||||
|
||||
const isValidContainer = (value) =>
|
||||
typeof value === "string" &&
|
||||
value.length >= 3 &&
|
||||
value.length <= 63 &&
|
||||
/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value);
|
||||
|
||||
const isValidDraftFolder = (value) =>
|
||||
typeof value === "string" &&
|
||||
value.length <= 100 &&
|
||||
!value.includes("..") &&
|
||||
/^[A-Za-z0-9](?:[A-Za-z0-9/-]*[A-Za-z0-9])?$/.test(value);
|
||||
|
||||
const forbidden = (res) =>
|
||||
respondError(res, {
|
||||
status: 403,
|
||||
code: "FORBIDDEN",
|
||||
message: "Request not permitted"
|
||||
});
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const shouldDownload = req.query.download === "true";
|
||||
const checkHash = req.query.hash;
|
||||
let reqBodyobj =
|
||||
typeof req.body === "string" ? JSON.parse(req.body) : req.body;
|
||||
let reqBodyobj;
|
||||
try {
|
||||
reqBodyobj =
|
||||
typeof req.body === "string" ? JSON.parse(req.body) : req.body;
|
||||
} catch (_error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_REQUEST",
|
||||
message: "Invalid request"
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!reqBodyobj ||
|
||||
typeof reqBodyobj !== "object" ||
|
||||
Array.isArray(reqBodyobj)
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_REQUEST",
|
||||
message: "Invalid request"
|
||||
});
|
||||
}
|
||||
|
||||
const cleanFilesList = (blobList) => {
|
||||
let newblobList = Array.isArray(blobList?.filesList)
|
||||
@@ -164,7 +206,52 @@ export default async function handler(req, res) {
|
||||
checkquerypath += "?download=true";
|
||||
}
|
||||
|
||||
if (typeof checkHash === "undefined" || checkHash.length === 0) {
|
||||
if (
|
||||
typeof containerID === "undefined" ||
|
||||
String(containerID).length === 0 ||
|
||||
typeof caseRef === "undefined" ||
|
||||
String(caseRef).length === 0
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
message: "containerID and case reference are required"
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!isValidContainer(containerID) ||
|
||||
!isValidDraftFolder(caseRef) ||
|
||||
(Object.prototype.hasOwnProperty.call(reqBodyobj, "ticketnumber") &&
|
||||
Object.prototype.hasOwnProperty.call(reqBodyobj, "caseRef") &&
|
||||
reqBodyobj.ticketnumber !== reqBodyobj.caseRef) ||
|
||||
(typeof req.query.download !== "undefined" &&
|
||||
typeof req.query.download !== "string") ||
|
||||
[
|
||||
"container",
|
||||
"containerID",
|
||||
"casefolderID",
|
||||
"caseRef",
|
||||
"ticketnumber"
|
||||
].some((key) => Object.prototype.hasOwnProperty.call(req.query, key))
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_REQUEST",
|
||||
message: "Invalid request"
|
||||
});
|
||||
}
|
||||
|
||||
const session = await getServerSession(req, res, authOptions(req, res));
|
||||
if (
|
||||
typeof session?.user?.id !== "string" ||
|
||||
session.user.id.length === 0 ||
|
||||
session.user.id !== containerID
|
||||
) {
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
if (typeof checkHash !== "string" || checkHash.length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "HASH_REQUIRED",
|
||||
|
||||
@@ -4795,77 +4795,450 @@ test("generateappealpdfcopy catch path returns APPEAL_PDF_COPY_GENERATION_FAILED
|
||||
);
|
||||
});
|
||||
|
||||
test("generatepdf catch path returns GENERATE_PDF_FAILED", async () => {
|
||||
const PDF_OWNER_CONTAINER = "owner-container";
|
||||
const PDF_CASE_FOLDER = "CASE-1";
|
||||
|
||||
const loadGeneratePdf = (overrides = {}) => {
|
||||
const { Readable } = require("stream");
|
||||
const calls = {
|
||||
session: [],
|
||||
options: [],
|
||||
hash: [],
|
||||
render: [],
|
||||
create: []
|
||||
};
|
||||
const options = { id: "generate-pdf-options" };
|
||||
const mod = loadModule("pages/api/file/generatepdf.js", {
|
||||
Buffer,
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
authOptions: (req, res) => {
|
||||
calls.options.push([req, res]);
|
||||
return options;
|
||||
},
|
||||
getServerSession: async (...args) => {
|
||||
calls.session.push(args);
|
||||
return { user: { id: PDF_OWNER_CONTAINER } };
|
||||
},
|
||||
hashAPIPath: (queryPath) => {
|
||||
calls.hash.push(queryPath);
|
||||
return queryPath.includes("?")
|
||||
? "&hash=expected"
|
||||
: "?hash=expected";
|
||||
},
|
||||
consoleLogger: () => {},
|
||||
ReactPDF: {
|
||||
renderToStream: async () => Readable.from(["pdf"])
|
||||
renderToStream: async (...args) => {
|
||||
calls.render.push(args);
|
||||
return Readable.from(["pdf"]);
|
||||
}
|
||||
},
|
||||
createRepPDFBlob: async () => {
|
||||
throw new Error("pdf write failed");
|
||||
createRepPDFBlob: async (...args) => {
|
||||
calls.create.push(args);
|
||||
return { id: "representation-pdf" };
|
||||
},
|
||||
other_pdf: () => ({ type: "other" })
|
||||
other_pdf: () => ({ type: "other" }),
|
||||
...overrides
|
||||
});
|
||||
|
||||
const req = {
|
||||
return { mod, calls, options };
|
||||
};
|
||||
|
||||
const generatePdfRequest = (overrides = {}) => ({
|
||||
method: "POST",
|
||||
query: { hash: "expected" },
|
||||
body: {
|
||||
representationType: "Other",
|
||||
repfile_name: "rep-file",
|
||||
containerID: PDF_OWNER_CONTAINER,
|
||||
caseRef: PDF_CASE_FOLDER
|
||||
},
|
||||
...overrides
|
||||
});
|
||||
|
||||
const loadGenerateAppealPdf = (overrides = {}) => {
|
||||
const { Readable } = require("stream");
|
||||
const calls = {
|
||||
session: [],
|
||||
options: [],
|
||||
hash: [],
|
||||
temp: [],
|
||||
progress: [],
|
||||
download: [],
|
||||
pickLists: [],
|
||||
render: [],
|
||||
create: []
|
||||
};
|
||||
const options = { id: "generate-appeal-pdf-options" };
|
||||
const mod = loadModule("pages/api/file/generateappealpdf.js", {
|
||||
Buffer,
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
authOptions: (req, res) => {
|
||||
calls.options.push([req, res]);
|
||||
return options;
|
||||
},
|
||||
getServerSession: async (...args) => {
|
||||
calls.session.push(args);
|
||||
return { user: { id: PDF_OWNER_CONTAINER } };
|
||||
},
|
||||
hashAPIPath: (queryPath) => {
|
||||
calls.hash.push(queryPath);
|
||||
return "&hash=expected";
|
||||
},
|
||||
getTempCaseBlob: async (...args) => {
|
||||
calls.temp.push(args);
|
||||
return { ticketnumber: PDF_CASE_FOLDER };
|
||||
},
|
||||
getProgressBlobs: async (...args) => {
|
||||
calls.progress.push(args);
|
||||
return { path: `${PDF_CASE_FOLDER}/progress.json` };
|
||||
},
|
||||
downloadProgressFile: async (...args) => {
|
||||
calls.download.push(args);
|
||||
return {
|
||||
pinswg_name: PDF_CASE_FOLDER,
|
||||
pinswg_appealcasetype: "846040000"
|
||||
};
|
||||
},
|
||||
getPickLists: async (...args) => {
|
||||
calls.pickLists.push(args);
|
||||
return {};
|
||||
},
|
||||
planningappeals78_pdf: () => ({ type: "planning" }),
|
||||
other_pdf: () => ({ type: "other" }),
|
||||
ReactPDF: {
|
||||
renderToStream: async (...args) => {
|
||||
calls.render.push(args);
|
||||
return Readable.from(["pdf"]);
|
||||
}
|
||||
},
|
||||
createAppealPDFBlob: async (...args) => {
|
||||
calls.create.push(args);
|
||||
return { id: "appeal-pdf" };
|
||||
},
|
||||
...overrides
|
||||
});
|
||||
|
||||
return { mod, calls, options };
|
||||
};
|
||||
|
||||
const generateAppealPdfRequest = (overrides = {}) => ({
|
||||
method: "POST",
|
||||
query: { hash: "expected", appealType: "846040000" },
|
||||
body: {
|
||||
containerID: PDF_OWNER_CONTAINER,
|
||||
casefolderID: PDF_CASE_FOLDER,
|
||||
filesList: []
|
||||
},
|
||||
...overrides
|
||||
});
|
||||
|
||||
const assertNoGeneratePdfWork = (calls) => {
|
||||
assert.strictEqual(calls.render.length, 0);
|
||||
assert.strictEqual(calls.create.length, 0);
|
||||
};
|
||||
|
||||
const assertNoGenerateAppealPdfWork = (calls) => {
|
||||
assert.strictEqual(calls.temp.length, 0);
|
||||
assert.strictEqual(calls.progress.length, 0);
|
||||
assert.strictEqual(calls.download.length, 0);
|
||||
assert.strictEqual(calls.pickLists.length, 0);
|
||||
assert.strictEqual(calls.render.length, 0);
|
||||
assert.strictEqual(calls.create.length, 0);
|
||||
};
|
||||
|
||||
for (const endpoint of [
|
||||
{
|
||||
name: "generatepdf",
|
||||
load: loadGeneratePdf,
|
||||
request: generatePdfRequest,
|
||||
assertNoWork: assertNoGeneratePdfWork
|
||||
},
|
||||
{
|
||||
name: "generateappealpdf",
|
||||
load: loadGenerateAppealPdf,
|
||||
request: generateAppealPdfRequest,
|
||||
assertNoWork: assertNoGenerateAppealPdfWork
|
||||
}
|
||||
]) {
|
||||
for (const sessionCase of [
|
||||
{ name: "missing session", session: null },
|
||||
{ name: "missing session user id", session: { user: {} } },
|
||||
{ name: "empty session user id", session: { user: { id: "" } } },
|
||||
{
|
||||
name: "non-string session user id",
|
||||
session: { user: { id: 123 } }
|
||||
}
|
||||
]) {
|
||||
test(`${endpoint.name} rejects ${sessionCase.name} before privileged work`, async () => {
|
||||
const loaded = endpoint.load({
|
||||
getServerSession: async () => sessionCase.session
|
||||
});
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(endpoint.request(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "FORBIDDEN");
|
||||
endpoint.assertNoWork(loaded.calls);
|
||||
});
|
||||
}
|
||||
|
||||
test(`${endpoint.name} rejects another user's container before privileged work`, async () => {
|
||||
const loaded = endpoint.load({
|
||||
getServerSession: async () => ({ user: { id: "different-container" } })
|
||||
});
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(endpoint.request(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "FORBIDDEN");
|
||||
endpoint.assertNoWork(loaded.calls);
|
||||
});
|
||||
|
||||
test(`${endpoint.name} ignores a client-provided user identity`, async () => {
|
||||
const loaded = endpoint.load({
|
||||
getServerSession: async () => ({ user: { id: "different-container" } })
|
||||
});
|
||||
const req = endpoint.request();
|
||||
req.body.userID = PDF_OWNER_CONTAINER;
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
endpoint.assertNoWork(loaded.calls);
|
||||
});
|
||||
|
||||
test(`${endpoint.name} passes req, res and request-scoped auth options to getServerSession`, async () => {
|
||||
const loaded = endpoint.load();
|
||||
const req = endpoint.request();
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(req, res);
|
||||
|
||||
assert.deepStrictEqual(loaded.calls.options, [[req, res]]);
|
||||
assert.deepStrictEqual(loaded.calls.session, [
|
||||
[req, res, loaded.options]
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
for (const containerCase of [
|
||||
{ name: "missing", value: undefined },
|
||||
{ name: "invalid", value: "Invalid_Container" },
|
||||
{ name: "ambiguous", value: [PDF_OWNER_CONTAINER] }
|
||||
]) {
|
||||
test(`generatepdf rejects ${containerCase.name} container before privileged work`, async () => {
|
||||
const loaded = loadGeneratePdf();
|
||||
const req = generatePdfRequest();
|
||||
if (typeof containerCase.value === "undefined") {
|
||||
delete req.body.containerID;
|
||||
} else {
|
||||
req.body.containerID = containerCase.value;
|
||||
}
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assertNoGeneratePdfWork(loaded.calls);
|
||||
});
|
||||
|
||||
test(`generateappealpdf rejects ${containerCase.name} container before privileged work`, async () => {
|
||||
const loaded = loadGenerateAppealPdf();
|
||||
const req = generateAppealPdfRequest();
|
||||
if (typeof containerCase.value === "undefined") {
|
||||
delete req.body.containerID;
|
||||
} else {
|
||||
req.body.containerID = containerCase.value;
|
||||
}
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assertNoGenerateAppealPdfWork(loaded.calls);
|
||||
});
|
||||
}
|
||||
|
||||
test("generatepdf rejects missing and conflicting draft-folder identifiers", async () => {
|
||||
for (const body of [
|
||||
{
|
||||
representationType: "Other",
|
||||
repfile_name: "rep-file",
|
||||
containerID: PDF_OWNER_CONTAINER
|
||||
},
|
||||
{
|
||||
representationType: "Other",
|
||||
repfile_name: "rep-file",
|
||||
containerID: PDF_OWNER_CONTAINER,
|
||||
ticketnumber: PDF_CASE_FOLDER,
|
||||
caseRef: "CASE-2"
|
||||
}
|
||||
]) {
|
||||
const loaded = loadGeneratePdf();
|
||||
const res = createRes();
|
||||
await loaded.mod.default(generatePdfRequest({ body }), res);
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assertNoGeneratePdfWork(loaded.calls);
|
||||
}
|
||||
});
|
||||
|
||||
test("generateappealpdf rejects missing or malformed draft-folder identifiers", async () => {
|
||||
for (const value of [undefined, "../CASE-1", [PDF_CASE_FOLDER]]) {
|
||||
const loaded = loadGenerateAppealPdf();
|
||||
const req = generateAppealPdfRequest();
|
||||
if (typeof value === "undefined") {
|
||||
delete req.body.casefolderID;
|
||||
} else {
|
||||
req.body.casefolderID = value;
|
||||
}
|
||||
const res = createRes();
|
||||
await loaded.mod.default(req, res);
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assertNoGenerateAppealPdfWork(loaded.calls);
|
||||
}
|
||||
});
|
||||
|
||||
for (const endpoint of [
|
||||
{
|
||||
name: "generatepdf",
|
||||
load: loadGeneratePdf,
|
||||
request: generatePdfRequest,
|
||||
assertNoWork: assertNoGeneratePdfWork
|
||||
},
|
||||
{
|
||||
name: "generateappealpdf",
|
||||
load: loadGenerateAppealPdf,
|
||||
request: generateAppealPdfRequest,
|
||||
assertNoWork: assertNoGenerateAppealPdfWork
|
||||
}
|
||||
]) {
|
||||
test(`${endpoint.name} rejects a query/body container ambiguity`, async () => {
|
||||
const loaded = endpoint.load();
|
||||
const req = endpoint.request();
|
||||
req.query.containerID = PDF_OWNER_CONTAINER;
|
||||
const res = createRes();
|
||||
await loaded.mod.default(req, res);
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
endpoint.assertNoWork(loaded.calls);
|
||||
});
|
||||
|
||||
test(`${endpoint.name} rejects missing HMAC before privileged work`, async () => {
|
||||
const loaded = endpoint.load();
|
||||
const req = endpoint.request();
|
||||
delete req.query.hash;
|
||||
const res = createRes();
|
||||
await loaded.mod.default(req, res);
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
endpoint.assertNoWork(loaded.calls);
|
||||
});
|
||||
|
||||
test(`${endpoint.name} rejects invalid HMAC before privileged work`, async () => {
|
||||
const loaded = endpoint.load();
|
||||
const req = endpoint.request();
|
||||
req.query.hash = "wrong";
|
||||
const res = createRes();
|
||||
await loaded.mod.default(req, res);
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH");
|
||||
endpoint.assertNoWork(loaded.calls);
|
||||
});
|
||||
|
||||
test(`${endpoint.name} rejects a valid HMAC for an unauthorised container`, async () => {
|
||||
const loaded = endpoint.load({
|
||||
getServerSession: async () => ({ user: { id: "different-container" } })
|
||||
});
|
||||
const res = createRes();
|
||||
await loaded.mod.default(endpoint.request(), res);
|
||||
assert.strictEqual(res.state.statusCode, 403);
|
||||
endpoint.assertNoWork(loaded.calls);
|
||||
});
|
||||
}
|
||||
|
||||
test("generatepdf owner success writes the PDF to the authorised container", async () => {
|
||||
const loaded = loadGeneratePdf();
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(generatePdfRequest(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 200);
|
||||
assert.strictEqual(loaded.calls.render.length, 1);
|
||||
assert.strictEqual(loaded.calls.create.length, 1);
|
||||
assert.strictEqual(loaded.calls.create[0][1], PDF_OWNER_CONTAINER);
|
||||
assert.strictEqual(loaded.calls.create[0][2], PDF_CASE_FOLDER);
|
||||
assert.strictEqual(loaded.calls.create[0][3], "rep-file");
|
||||
assert.strictEqual(res.state.jsonBody.status, "success");
|
||||
});
|
||||
|
||||
test("generateappealpdf owner success reads and writes only the authorised container", async () => {
|
||||
const loaded = loadGenerateAppealPdf();
|
||||
const res = createRes();
|
||||
|
||||
await loaded.mod.default(generateAppealPdfRequest(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 200);
|
||||
assert.strictEqual(loaded.calls.temp[0][0], PDF_OWNER_CONTAINER);
|
||||
assert.strictEqual(loaded.calls.progress[0][0], PDF_OWNER_CONTAINER);
|
||||
assert.strictEqual(loaded.calls.download[0][0], PDF_OWNER_CONTAINER);
|
||||
assert.strictEqual(loaded.calls.create[0][1], PDF_OWNER_CONTAINER);
|
||||
assert.strictEqual(loaded.calls.create[0][2], PDF_CASE_FOLDER);
|
||||
assert.strictEqual(res.state.jsonBody.status, "success");
|
||||
});
|
||||
|
||||
test("generatepdf catch path returns GENERATE_PDF_FAILED for an authorised owner", async () => {
|
||||
const loaded = loadGeneratePdf({
|
||||
createRepPDFBlob: async (...args) => {
|
||||
loaded.calls.create.push(args);
|
||||
throw new Error("pdf write failed");
|
||||
}
|
||||
});
|
||||
const req = generatePdfRequest({
|
||||
query: { hash: "expected", download: "true" },
|
||||
body: {
|
||||
representationType: "Other",
|
||||
repfile_name: "rep-file",
|
||||
containerID: "c1",
|
||||
casefolderID: "f1",
|
||||
caseRef: "CAS-1"
|
||||
containerID: PDF_OWNER_CONTAINER,
|
||||
caseRef: PDF_CASE_FOLDER
|
||||
}
|
||||
};
|
||||
});
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
await loaded.mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "GENERATE_PDF_FAILED");
|
||||
assert.strictEqual(loaded.calls.render.length, 1);
|
||||
assert.strictEqual(loaded.calls.create.length, 1);
|
||||
});
|
||||
|
||||
test("generateappealpdf catch path returns GENERATE_APPEAL_PDF_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/file/generateappealpdf.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
getTempCaseBlob: async () => ({ ticketnumber: "CAS-1" }),
|
||||
getProgressBlobs: async () => ({ path: "CAS-1/progress.json" }),
|
||||
test("generateappealpdf catch path returns GENERATE_APPEAL_PDF_FAILED for an authorised owner", async () => {
|
||||
const loaded = loadGenerateAppealPdf({
|
||||
downloadProgressFile: async () => {
|
||||
loaded.calls.download.push([
|
||||
PDF_OWNER_CONTAINER,
|
||||
`${PDF_CASE_FOLDER}/progress.json`,
|
||||
PDF_CASE_FOLDER
|
||||
]);
|
||||
throw new Error("progress failed");
|
||||
},
|
||||
getPickLists: async () => ({}),
|
||||
planningappeals78_pdf: () => ({ type: "planning" }),
|
||||
other_pdf: () => ({ type: "other" }),
|
||||
ReactPDF: {
|
||||
renderToStream: async () => {
|
||||
throw new Error("render failed");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const req = {
|
||||
query: { hash: "expected", appealType: "846040000" },
|
||||
body: {
|
||||
containerID: "c1",
|
||||
casefolderID: "f1",
|
||||
caseRef: "CAS-1",
|
||||
filesList: []
|
||||
}
|
||||
};
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
await loaded.mod.default(generateAppealPdfRequest(), res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"GENERATE_APPEAL_PDF_FAILED"
|
||||
);
|
||||
assert.strictEqual(loaded.calls.temp.length, 1);
|
||||
assert.strictEqual(loaded.calls.progress.length, 1);
|
||||
assert.strictEqual(loaded.calls.download.length, 1);
|
||||
assert.strictEqual(loaded.calls.render.length, 0);
|
||||
assert.strictEqual(loaded.calls.create.length, 0);
|
||||
});
|
||||
|
||||
const run = async () => {
|
||||
|
||||
Reference in New Issue
Block a user