Merge pull request #562 from OpenSignLabs/patch-1

fix: remove public access from do space and generate signed url wherever PDF files are accessed
This commit is contained in:
Amol
2024-04-05 21:30:14 +05:30
committed by GitHub
24 changed files with 237 additions and 33 deletions
@@ -149,7 +149,7 @@ const DashboardCard = (props) => {
}
})
.then((res) => {
const listData = res.data?.result.filter(
const listData = res.data?.result?.filter(
(x) => x.Signers.length > 0
);
let arr = [];
@@ -97,11 +97,13 @@ function DashboardReport(props) {
setIsMoreDocs(false);
}
setIsNextRecord(false);
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
if (!res.data.result.error) {
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
}
}
setIsLoader(false);
} catch (err) {
@@ -110,6 +112,7 @@ function DashboardReport(props) {
console.log("err ", err);
setIsLoader(false);
}
setIsLoader(false);
}
} else {
setIsLoader(false);
@@ -1,5 +1,6 @@
import React, { useState } from "react";
import "../../styles/AddUser.css";
import { getFileName } from "../../constant/Utils";
// import SelectFolder from "../../premitives/SelectFolder";
const EditTemplate = ({ template, onSuccess }) => {
@@ -45,7 +46,7 @@ const EditTemplate = ({ template, onSuccess }) => {
fontWeight: "700"
}}
>
{template.URL?.split("/")?.pop()?.split("_")[1]}
{getFileName(template.URL)}
</div>
</div>
<div className="form-section">
@@ -41,7 +41,7 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
<img
className="w-full h-full object-cover"
className="w-full h-full object-contain"
src={image}
alt="Profile"
/>
+12 -2
View File
@@ -1211,8 +1211,8 @@ export const multiSignEmbed = async (
position.type === radioButtonWidget
? 10
: position.type === "checkbox"
? 10
: newUpdateHeight;
? 10
: newUpdateHeight;
const newHeight = ind ? (ind > 0 ? widgetHeight : 0) : widgetHeight;
if (signyourself) {
@@ -1777,6 +1777,16 @@ export const handleCopyNextToWidget = (
setXyPostion(updatePlaceholder);
}
};
export const getFileName = (fileUrl) => {
if (fileUrl) {
const url = new URL(fileUrl);
const filename = url.pathname.substring(url.pathname.indexOf("_") + 1);
return filename || "";
} else {
return "";
//fetch tenant app logo from `partners_Tenant` class by domain name
export const getAppLogo = async () => {
const domainName = window.location.host;
+1
View File
@@ -36,6 +36,7 @@ const HomeLayout = () => {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
localStorage.setItem("profileImg", user.get('ProfilePic'));
checkIsSubscribed();
} else {
setIsUserValid(false);
+4 -5
View File
@@ -11,6 +11,7 @@ import SignersInput from "../components/shared/fields/SignersInput";
import Title from "../components/Title";
import PageNotFound from "./PageNotFound";
import { SaveFileSize } from "../constant/saveFileSize";
import { getFileName } from "../constant/Utils";
// `Form` render all type of Form on this basis of their provided in path
function Form() {
@@ -254,6 +255,7 @@ const Forms = (props) => {
setpercentage(0);
setTimeout(() => setIsReset(false), 50);
};
return (
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
<Title title={props?.title} />
@@ -298,13 +300,10 @@ const Forms = (props) => {
<div className="flex gap-2 justify-center items-center">
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
<div className="break-all">
file selected :{" "}
{fileupload?.split("/")?.pop()?.split("_")[1]}
file selected : {getFileName(fileload)}
</div>
<div
onClick={() => {
setFileUpload([]);
}}
onClick={() => setFileUpload([])}
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
>
<i className="fa-solid fa-xmark"></i>
+9 -7
View File
@@ -54,7 +54,7 @@ const Report = () => {
setHeading(json.heading);
setReportName(json.reportName);
setForm(json.form);
setReportHelp(json?.helpMsg)
setReportHelp(json?.helpMsg);
const currentUser = Parse.User.current().id;
const headers = {
@@ -106,12 +106,14 @@ const Report = () => {
} else {
setIsMoreDocs(false);
}
setIsNextRecord(false);
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
if (!res.data.result.error) {
setIsNextRecord(false);
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
}
}
setIsLoader(false);
} catch (err) {
+1 -1
View File
@@ -66,6 +66,7 @@ function UserProfile() {
SetPhone(res.phone);
setImage(res.ProfilePic);
localStorage.setItem("username", res.name);
localStorage.setItem("profileImg", res.ProfilePic);
await updateExtUser({ Name: res.name, Phone: res.phone });
alert("Profile updated successfully.");
setEditMode(false);
@@ -147,7 +148,6 @@ function UserProfile() {
// console.log("File URL:", response.url());
if (response.url()) {
setImage(response.url());
localStorage.setItem("profileImg", response.url());
setpercentage(0);
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, response.url(), tenantId);
@@ -199,7 +199,7 @@ const ReportTable = ({
const indexOfLastDoc = currentPage * docPerPage;
const indexOfFirstDoc = indexOfLastDoc - docPerPage;
// `currentLists` is total record render on current page
const currentLists = List.slice(indexOfFirstDoc, indexOfLastDoc);
const currentLists = List?.slice(indexOfFirstDoc, indexOfLastDoc);
// Change page
const paginateFront = () => setCurrentPage(currentPage + 1);
@@ -180,7 +180,7 @@ export default async function createDocumentwithCoordinate(request, response) {
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const signOpt = { name: 'signature', status: 'required' };
const widgetData = {
isStamp: widget.type === 'stamp',
isStamp: widget.type === 'stamp'|| widget.type === 'image',
key: randomId(),
isDrag: false,
scale: 1,
@@ -136,7 +136,7 @@ export default async function createTemplatewithCoordinate(request, response) {
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const signOpt = { name: 'signature', status: 'required' };
const widgetData = {
isStamp: widget.type === 'stamp',
isStamp: widget.type === 'stamp'|| widget.type === 'image',
key: randomId(),
isDrag: false,
scale: 1,
+8
View File
@@ -25,6 +25,10 @@ import callWebhook from './parsefunction/callWebhook.js';
import SubscribeFree from './parsefunction/SubscribeFree.js';
import DocumentBeforesave from './parsefunction/DocumentBeforesave.js';
import TemplateBeforeSave from './parsefunction/TemplateBeforesave.js';
import DocumentBeforeFind from './parsefunction/DocumentAfterFind.js';
import TemplateAfterFind from './parsefunction/TemplateAfterFind.js';
import UserAfterFind from './parsefunction/UserAfterFInd.js';
import SignatureAfterFind from './parsefunction/SignatureAfterFind.js';
Parse.Cloud.define('AddUserToRole', addUserToGroups);
Parse.Cloud.define('UserGroups', getUserGroups);
@@ -53,3 +57,7 @@ Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave);
Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave);
Parse.Cloud.beforeSave('contracts_Template', TemplateBeforeSave);
Parse.Cloud.afterFind(Parse.User, UserAfterFind);
Parse.Cloud.afterFind('contracts_Document', DocumentBeforeFind);
Parse.Cloud.afterFind('contracts_Template', TemplateAfterFind);
Parse.Cloud.afterFind("contracts_Signature", SignatureAfterFind);
@@ -0,0 +1,24 @@
import getPresignedUrl from './getSignedUrl.js';
async function DocumentAfterFind(request) {
if (request.objects.length === 1) {
if (request.objects) {
const obj = request.objects[0];
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
const Url = obj?.get('URL') && obj?.get('URL');
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
if (SignedUrl) {
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
}
if (Url) {
obj.set('URL', getPresignedUrl(Url));
}
if (certificateUrl) {
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
}
return [obj];
}
}
}
export default DocumentAfterFind;
@@ -0,0 +1,22 @@
import getPresignedUrl from './getSignedUrl.js';
async function SignatureAfterFind(request) {
if (request.objects.length === 1) {
if (request.objects) {
const obj = request.objects[0];
const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL');
const Initials = obj?.get('Initials') && obj?.get('Initials');
if (ImageURL) {
obj.set('ImageURL', getPresignedUrl(ImageURL));
}
if (Initials) {
obj.set('Initials', getPresignedUrl(Initials));
}
return [obj];
}
}
}
export default SignatureAfterFind;
@@ -0,0 +1,24 @@
import getPresignedUrl from './getSignedUrl.js';
async function TemplateAfterFind(request) {
if (request.objects.length === 1) {
if (request.objects) {
const obj = request.objects[0];
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
const Url = obj?.get('URL') && obj?.get('URL');
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
if (SignedUrl) {
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
}
if (Url) {
obj.set('URL', getPresignedUrl(Url));
}
if (certificateUrl) {
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
}
return [obj];
}
}
}
export default TemplateAfterFind;
@@ -0,0 +1,17 @@
import getPresignedUrl from './getSignedUrl.js';
async function UserAfterFind(request) {
if (request.objects.length === 1) {
if (request.objects) {
const obj = request.objects[0];
const ProfilePic = obj?.get('ProfilePic') && obj?.get('ProfilePic');
if (ProfilePic) {
obj.set('ProfilePic', getPresignedUrl(ProfilePic));
}
return [obj];
}
}
}
export default UserAfterFind;
@@ -21,7 +21,7 @@ export default async function getDocument(request) {
query.include('Signers');
query.include('AuditTrail.UserPtr');
query.include('Placeholders');
query.notEqualTo('IsArchive', true)
query.notEqualTo('IsArchive', true);
const res = await query.first({ useMasterKey: true });
if (res) {
const acl = res.getACL();
@@ -42,7 +42,7 @@ export default async function getReport(request) {
}
}
} catch (err) {
console.log('err', err);
console.log('err', err.message);
if (err.code == 209) {
return { error: 'Invalid session token' };
} else {
@@ -0,0 +1,26 @@
import AWS from 'aws-sdk';
const credentials = {
accessKeyId: process.env.DO_ACCESS_KEY_ID,
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY,
};
AWS.config.update({ credentials: credentials, region: process.env.DO_REGION });
const spacesEndpoint = new AWS.Endpoint(process.env.DO_ENDPOINT);
const s3 = new AWS.S3({ endpoint: spacesEndpoint });
export default function getPresignedUrl(url) {
// Create a new URL object
const parsedUrl = new URL(url);
// Get the pathname of the URL
const pathname = parsedUrl.pathname;
// Extract the filename from the pathname
const filename = pathname.substring(pathname.lastIndexOf('/') + 1);
// presignedGETURL return presignedUrl with expires time
const presignedGETURL = s3.getSignedUrl('getObject', {
Bucket: process.env.DO_SPACE,
Key: filename, //filename
Expires: 160, //time to expire in seconds
});
return presignedGETURL;
}
+4 -4
View File
@@ -11,7 +11,7 @@ const serverUrl = process.env.SERVER_URL,
async function uploadFile(e, a) {
try {
var t = fs.readFileSync(a),
s = new Parse.File(e, [...t]),
s = new Parse.File(e, [...t], 'application/pdf'),
r = (await s.save({ useMasterKey: !0 }), s.url());
return { imageUrl: r };
} catch (e) {
@@ -111,11 +111,11 @@ async function sendDoctoWebhook(t, e, a, s) {
}),
a.save(null, { useMasterKey: !0 });
} catch (e) {
console.log('err save in contracts_Webhook', e);
console.log('err save in contracts_Webhook', e.message);
}
})
.catch(e => {
console.log('Err send data to webhook', e);
console.log('Err send data to webhook', e.message);
try {
var a = new Parse.Object('contracts_Webhook');
a.set('Log', e?.status),
@@ -126,7 +126,7 @@ async function sendDoctoWebhook(t, e, a, s) {
}),
a.save(null, { useMasterKey: !0 });
} catch (e) {
console.log('err save in contracts_Webhook', e);
console.log('err save in contracts_Webhook', e.message);
}
}));
}
+3 -1
View File
@@ -13,7 +13,7 @@ import http from 'http';
import formData from 'form-data';
import Mailgun from 'mailgun.js';
import { ApiPayloadConverter } from 'parse-server-api-mail-adapter';
import S3Adapter from 'parse-server-s3-adapter';
import S3Adapter from '@parse/s3-files-adapter';
import FSFilesAdapter from 'parse-server-fs-adapter';
import AWS from 'aws-sdk';
import { app as customRoute } from './cloud/customRoute/customApp.js';
@@ -32,6 +32,8 @@ if (process.env.USE_LOCAL !== 'TRUE') {
region: process.env.DO_REGION,
directAccess: true,
preserveFileName: true,
presignedUrl: true,
presignedUrlExpires: 900,
s3overrides: {
accessKeyId: process.env.DO_ACCESS_KEY_ID,
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY,
+64
View File
@@ -9,6 +9,7 @@
"version": "1.4.0",
"license": "MIT",
"dependencies": {
"@parse/s3-files-adapter": "^2.2.0",
"aws-sdk": "^2.1581.0",
"axios": "^1.6.8",
"cors": "^2.8.5",
@@ -2560,6 +2561,69 @@
}
}
},
"node_modules/@parse/s3-files-adapter": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@parse/s3-files-adapter/-/s3-files-adapter-2.2.0.tgz",
"integrity": "sha512-y+d7+AvmRUrROe4kJOF8128yUSxieM53vXQsSaG2gvY9VgPScfpfho0pE7VZ5mLumzxJjmwW6oufiGdgcjgcTA==",
"dependencies": {
"aws-sdk": "2.1363.0"
}
},
"node_modules/@parse/s3-files-adapter/node_modules/aws-sdk": {
"version": "2.1363.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1363.0.tgz",
"integrity": "sha512-M2MZZXehgi/EMQv5GlzRkn3TlhoOYHg2cYdSAAqhjv67WaEG50MjaQy5vRvfN1i8XvB24aJFJ5pCrx69TaCaIg==",
"dependencies": {
"buffer": "4.9.2",
"events": "1.1.1",
"ieee754": "1.1.13",
"jmespath": "0.16.0",
"querystring": "0.2.0",
"sax": "1.2.1",
"url": "0.10.3",
"util": "^0.12.4",
"uuid": "8.0.0",
"xml2js": "0.5.0"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/@parse/s3-files-adapter/node_modules/buffer": {
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
"dependencies": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4",
"isarray": "^1.0.0"
}
},
"node_modules/@parse/s3-files-adapter/node_modules/ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
},
"node_modules/@parse/s3-files-adapter/node_modules/uuid": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz",
"integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/@parse/s3-files-adapter/node_modules/xml2js": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
"integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/@pdf-lib/standard-fonts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
+1
View File
@@ -18,6 +18,7 @@
"watch": "nodemon index.js"
},
"dependencies": {
"@parse/s3-files-adapter": "^2.2.0",
"aws-sdk": "^2.1581.0",
"axios": "^1.6.8",
"cors": "^2.8.5",