mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-20 06:35:54 +02:00
v2.12.0
feat: create duplicate template functionality feat: add support of csv and xlsx for bulk contacts import feat: add info text for forms and dashboard button feat: implement functionality to delete a PDF page while editing the document feat: show completed documents in which signers included but not owner in completed reports feat: add delete folder button in opensign drive feat: introduce Bcc email support for sending completed documents feat: allow merging multiple pdf while drafting document and template feat: add 'My Initials' tab for auto-signing in request-sign flow feat: add support for redirect URL to navigate after document completion feat: introduce privacy policy and digital signature terms before signing documents. feat: add feature to allow adding new pages to existing document feat: add save signature, initials, stamp functionality in sign pad for logged in user feat: add preferences menu feat: add save custom email template, notifyonsignature, set timezone, allow signature types in preferences feat: secure local url feat: implement Italian language translation feat: implement German language translation feat: update menu name from report to documents and shift contactbook in main menu feat: provide edit contact functionality fix: unable to delete folder when all its documents are deleted fix: fields.push is not function fix: document loading issue in opensign drive fix: adjust the guest signature flow to display the document in full screen, eliminating any blank space which is displayed below the place holder in mobile view fix: resolve issue of instance of pdfdict or pdfstream but got undefined build(deps): update dependencies refactor: change note text from add contact form
This commit is contained in:
@@ -1,29 +1,47 @@
|
||||
import AWS from 'aws-sdk';
|
||||
import { useLocal } from '../../Utils.js';
|
||||
export default function getPresignedUrl(url, adapter) {
|
||||
const credentials = {
|
||||
accessKeyId: adapter?.accessKeyId || process.env.DO_ACCESS_KEY_ID,
|
||||
secretAccessKey: adapter?.secretAccessKey || process.env.DO_SECRET_ACCESS_KEY,
|
||||
};
|
||||
AWS.config.update({ credentials: credentials, region: adapter?.region || process.env.DO_REGION });
|
||||
const spacesEndpoint = adapter?.endpoint || new AWS.Endpoint(process.env.DO_ENDPOINT);
|
||||
import jwt from 'jsonwebtoken';
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const s3 = new AWS.S3({ endpoint: spacesEndpoint, signatureVersion: "v4" });
|
||||
export default function getPresignedUrl(
|
||||
url,
|
||||
) {
|
||||
if (url?.includes('files')) {
|
||||
return presignedlocalUrl(url);
|
||||
} else {
|
||||
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);
|
||||
|
||||
// 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);
|
||||
const s3 = new AWS.S3({ endpoint: spacesEndpoint, signatureVersion: 'v4' });
|
||||
|
||||
// presignedGETURL return presignedUrl with expires time
|
||||
const presignedGETURL = s3.getSignedUrl('getObject', {
|
||||
Bucket: adapter?.bucketName || process.env.DO_SPACE,
|
||||
Key: filename, //filename
|
||||
Expires: 160, //time to expire in seconds
|
||||
});
|
||||
return presignedGETURL;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSignedUrl(request) {
|
||||
@@ -31,10 +49,13 @@ export async function getSignedUrl(request) {
|
||||
const docId = request.params.docId || '';
|
||||
const templateId = request.params.templateId || '';
|
||||
const url = request.params.url;
|
||||
const fileAdapterId = request.params.fileAdapterId || '';
|
||||
if (docId || templateId) {
|
||||
try {
|
||||
if (fileAdapterId || useLocal !== 'true') {
|
||||
if (url?.includes('files')) {
|
||||
return presignedlocalUrl(url);
|
||||
} else if (
|
||||
useLocal !== 'true'
|
||||
) {
|
||||
const query = new Parse.Query(docId ? 'contracts_Document' : 'contracts_Template');
|
||||
query.equalTo('objectId', docId ? docId : templateId);
|
||||
query.include('ExtUserPtr.TenantId');
|
||||
@@ -49,26 +70,15 @@ export async function getSignedUrl(request) {
|
||||
'User is not authenticated.'
|
||||
);
|
||||
} else {
|
||||
let adapterConfig = {};
|
||||
if (fileAdapterId) {
|
||||
// `adapterConfig` is used to get file in user's fileAdapter
|
||||
adapterConfig =
|
||||
_resDoc?.ExtUserPtr?.TenantId?.FileAdapters?.find(
|
||||
x => x.id === fileAdapterId
|
||||
) || {};
|
||||
}
|
||||
const presignedUrl = getPresignedUrl(url, adapterConfig);
|
||||
const presignedUrl = getPresignedUrl(
|
||||
url,
|
||||
);
|
||||
return presignedUrl;
|
||||
}
|
||||
} else {
|
||||
let adapterConfig = {};
|
||||
if (fileAdapterId) {
|
||||
// `adapterConfig` is used to get file in user's fileAdapter
|
||||
adapterConfig =
|
||||
_resDoc?.ExtUserPtr?.TenantId?.FileAdapters?.find(x => x.id === fileAdapterId) ||
|
||||
{};
|
||||
}
|
||||
const presignedUrl = getPresignedUrl(url, adapterConfig);
|
||||
const presignedUrl = getPresignedUrl(
|
||||
url,
|
||||
);
|
||||
return presignedUrl;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +93,9 @@ export async function getSignedUrl(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
} else {
|
||||
if (useLocal !== 'true') {
|
||||
if (url?.includes('files')) {
|
||||
return presignedlocalUrl(url);
|
||||
} else if (useLocal !== 'true') {
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
} else {
|
||||
@@ -99,3 +111,71 @@ export async function getSignedUrl(request) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to generate a signed URL with JWT
|
||||
export function getSignedLocalUrl(fileUrl, expirationTimeInSeconds) {
|
||||
const secretKey = process.env.MASTER_KEY;
|
||||
const exp = expirationTimeInSeconds || 200;
|
||||
try {
|
||||
// Create the payload with the file URL and expiration time
|
||||
const payload = {
|
||||
fileUrl,
|
||||
exp: Math.floor(Date.now() / 1000) + exp, // Expiry time in seconds
|
||||
};
|
||||
|
||||
// Generate the JWT token
|
||||
const token = jwt.sign(payload, secretKey);
|
||||
// Return the signed URL containing the token
|
||||
return `${fileUrl}?token=${token}`;
|
||||
} catch (err) {
|
||||
console.log('Err while siging local url', err);
|
||||
throw new Error('Invalid or expired token.');
|
||||
}
|
||||
}
|
||||
|
||||
export function presignedlocalUrl(signedUrl, expirationTimeInSeconds) {
|
||||
if (signedUrl?.includes('files')) {
|
||||
const fileUrl = signedUrl.split('?')?.[0];
|
||||
const secretKey = process.env.MASTER_KEY;
|
||||
const exp = expirationTimeInSeconds || 200;
|
||||
try {
|
||||
// Create the payload with the file URL and expiration time
|
||||
const payload = {
|
||||
fileUrl,
|
||||
exp: Math.floor(Date.now() / 1000) + exp, // Expiry time in seconds
|
||||
};
|
||||
// Generate the JWT token
|
||||
const token = jwt.sign(payload, secretKey);
|
||||
// Return the signed URL containing the token
|
||||
return `${fileUrl}?token=${token}`;
|
||||
} catch (err) {
|
||||
throw new Error('Invalid or expired token.');
|
||||
}
|
||||
} else {
|
||||
return signedUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to validate the signed URL
|
||||
export async function validateSignedLocalUrl(signedUrl) {
|
||||
const urlParams = new URLSearchParams(signedUrl.split('?')[1]);
|
||||
const token = urlParams.get('token');
|
||||
try {
|
||||
if (!token) {
|
||||
throw new Error('No token provided.');
|
||||
}
|
||||
const secretKey = process.env.MASTER_KEY;
|
||||
// Now verify the token (validate signature and expiration automatically)
|
||||
const decoded = jwt.verify(token, secretKey);
|
||||
// Check if the file URL in the JWT matches the requested file URL
|
||||
const fileUrl = signedUrl.split('?')[0];
|
||||
if (decoded.fileUrl !== fileUrl) {
|
||||
throw new Error('Invalid file URL in token.');
|
||||
}
|
||||
// If the token is valid and not expired, return the file URL
|
||||
return signedUrl;
|
||||
} catch (error) {
|
||||
console.log('Error validating file', error.message);
|
||||
return 'Unauthorized';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user