Skip to content

Commit

Permalink
Adding support for multi-tenancy
Browse files Browse the repository at this point in the history
  • Loading branch information
danjoa committed Dec 21, 2024
1 parent 376a8c0 commit 5cd7b12
Showing 1 changed file with 68 additions and 63 deletions.
131 changes: 68 additions & 63 deletions lib/plugin.js
Original file line number Diff line number Diff line change
@@ -1,108 +1,113 @@
const cds = require("@sap/cds/lib");
const LOG = cds.log("attachments");
const { extname } = require("path");
const DEBUG = LOG._debug ? LOG.debug : undefined;
const attachmentIDRegex = /\/\w+\(.*ID=([0-9a-fA-F-]{36})/;

cds.on("loaded", function unfoldModel(csn) {
if (!("Attachments" in csn.definitions)) return;
const csnCopy = structuredClone(csn)
cds.linked(csnCopy).forall("Composition", (comp) => {
const cds = require("@sap/cds/lib")
const LOG = cds.log("attachments")
const { extname } = require("path")
const DEBUG = LOG._debug ? LOG.debug : undefined
const attachmentIDRegex = /\/\w+\(.*ID=([0-9a-fA-F-]{36})/

cds.on("compile.to.edmx", unfoldModel)
cds.on("loaded", unfoldModel)

function unfoldModel (csn) {
const meta = csn.meta ??= {}
if (!("Attachments" in csn.definitions)) return
if (meta._enhanced_for_attachments) return
// const csnCopy = structuredClone(csn) // REVISIT: Why did we add this cloning?
cds.linked(csn).forall("Composition", (comp) => {
if (comp._target && comp._target["@_is_media_data"] && comp.parent && comp.is2many) {
const parentDefinition = comp.parent.name
let facets = csn.definitions[parentDefinition]["@UI.Facets"];
if (!facets) return;
DEBUG?.("Adding @UI.Facet to:", comp.parent.name);
let facets = comp.parent["@UI.Facets"]
if (!facets) return
DEBUG?.("Adding @UI.Facet to:", comp.parent.name)
facets.push({
$Type: "UI.ReferenceFacet",
Target: `${comp.name}/@UI.LineItem`,
Label: "{i18n>Attachments}",
});
})
}
});
});
})
meta._enhanced_for_attachments = true
}

cds.once("served", async function registerPluginHandlers() {
if (!("Attachments" in cds.model.definitions)) return;
cds.once("served", async function registerPluginHandlers () {
if (!("Attachments" in cds.model.definitions)) return

const AttachmentsSrv = await cds.connect.to("attachments");
const AttachmentsSrv = await cds.connect.to("attachments")

// Searching all associations to attachments to add respective handlers
for (let srv of cds.services) {
if (srv instanceof cds.ApplicationService) {
Object.values(srv.entities).forEach((entity) => {

for (let elementName in entity.elements) {
if (elementName === "SiblingEntity") continue; // REVISIT: Why do we have this?
const element = entity.elements[elementName], target = element._target;
if (elementName === "SiblingEntity") continue // REVISIT: Why do we have this?
const element = entity.elements[elementName], target = element._target
if (target?.["@_is_media_data"] && target?.drafts) {
DEBUG?.("serving attachments for:", target.name);
srv.before("READ", [target, target.drafts], validateAttachment);
DEBUG?.("serving attachments for:", target.name)

srv.before("READ", [target, target.drafts], validateAttachment)

srv.after("READ", [target, target.drafts], readAttachment);
srv.after("READ", [target, target.drafts], readAttachment)

srv.before("PUT", target.drafts, (req) => validateAttachmentSize(req) );
srv.before("PUT", target.drafts, (req) => validateAttachmentSize(req))

AttachmentsSrv.registerUpdateHandlers(srv, entity, target)

AttachmentsSrv.registerUpdateHandlers(srv, entity, target);

srv.before('NEW', target.drafts, req => {
req.data.url = cds.utils.uuid();
req.data.ID = cds.utils.uuid();
let ext = extname(req.data.filename).toLowerCase().slice(1);
req.data.mimeType = Ext2MimeTyes[ext] || "application/octet-stream";
});
req.data.url = cds.utils.uuid()
req.data.ID = cds.utils.uuid()
let ext = extname(req.data.filename).toLowerCase().slice(1)
req.data.mimeType = Ext2MimeTyes[ext] || "application/octet-stream"
})
}
}
});
})
}
}

async function validateAttachment(req) {
async function validateAttachment (req) {

/* removing case condition for mediaType annotation as in our case binary value and metadata is stored in different database */

req?.query?.SELECT?.columns?.forEach((element) => {
if(element.as === '[email protected]' && element.xpr){
delete element.xpr;
element.ref = ['mimeType'];
if (element.as === '[email protected]' && element.xpr) {
delete element.xpr
element.ref = ['mimeType']
}
});
})

if(req?.req?.url?.endsWith("/content")) {
const attachmentID = req.req.url.match(attachmentIDRegex)[1];
const status = await AttachmentsSrv.getStatus(req.target, { ID : attachmentID });
if (req?.req?.url?.endsWith("/content")) {
const attachmentID = req.req.url.match(attachmentIDRegex)[1]
const status = await AttachmentsSrv.getStatus(req.target, { ID: attachmentID })
const scanEnabled = cds.env.requires?.attachments?.scan ?? true
if(scanEnabled && status !== 'Clean') {
req.reject(403, 'Unable to download the attachment as scan status is not clean.');
if (scanEnabled && status !== 'Clean') {
req.reject(403, 'Unable to download the attachment as scan status is not clean.')
}
}
}

async function readAttachment([attachment], req) {
if (!req?.req?.url?.endsWith("/content") || !attachment || attachment?.content) return;
let keys = { ID : req.req.url.match(attachmentIDRegex)[1]};
let { target } = req;
attachment.content = await AttachmentsSrv.get(target, keys, req); //Dependency -> sending req object for usage in SDM plugin
async function readAttachment ([attachment], req) {
if (!req?.req?.url?.endsWith("/content") || !attachment || attachment?.content) return
let keys = { ID: req.req.url.match(attachmentIDRegex)[1] }
let { target } = req
attachment.content = await AttachmentsSrv.get(target, keys, req) //Dependency -> sending req object for usage in SDM plugin
}
});
})

function validateAttachmentSize(req) {
const contentLengthHeader = req.headers["content-length"];
let fileSizeInBytes;
function validateAttachmentSize (req) {
const contentLengthHeader = req.headers["content-length"]
let fileSizeInBytes

if (contentLengthHeader) {
fileSizeInBytes = Number(contentLengthHeader);
const MAX_FILE_SIZE = 419430400; //400 MB in bytes
fileSizeInBytes = Number(contentLengthHeader)
const MAX_FILE_SIZE = 419430400 //400 MB in bytes
if (fileSizeInBytes > MAX_FILE_SIZE) {
return req.reject(403, "File Size limit exceeded beyond 400 MB.");
return req.reject(403, "File Size limit exceeded beyond 400 MB.")
}
} else {
return req.reject(403, "Invalid Content Size");
return req.reject(403, "Invalid Content Size")
}
}

module.exports = { validateAttachmentSize };
module.exports = { validateAttachmentSize }

const Ext2MimeTyes = {
aac: "audio/aac",
Expand Down Expand Up @@ -164,4 +169,4 @@ const Ext2MimeTyes = {
zip: "application/zip",
txt: "application/txt",
lst: "application/txt"
};
}

0 comments on commit 5cd7b12

Please sign in to comment.