mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
195 lines
6.1 KiB
JavaScript
195 lines
6.1 KiB
JavaScript
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
|
|
import { getSignedUrl as presign } from '@aws-sdk/s3-request-presigner';
|
|
import { useLocal } from '../../Utils.js';
|
|
import jwt from 'jsonwebtoken';
|
|
import dotenv from 'dotenv';
|
|
import { isAuthenticated } from '../../utils/AuthUtils.js';
|
|
dotenv.config({ quiet: true });
|
|
|
|
function extractKeyFromUrl(url) {
|
|
// Create a new URL object
|
|
const parsedUrl = new URL(url);
|
|
// Get the pathname of the URL
|
|
const pathname = parsedUrl.pathname; // e.g. /mybucket/path/to/file.pdf (depends on baseUrl style)
|
|
// Extract the filename from the pathname
|
|
const filename = pathname.substring(pathname.lastIndexOf('/') + 1);
|
|
return filename;
|
|
}
|
|
|
|
function makeEndpoint(endpoint) {
|
|
if (!endpoint) return '';
|
|
|
|
if (endpoint.startsWith('http://') || endpoint.startsWith('https://')) {
|
|
return endpoint;
|
|
}
|
|
|
|
return `https://${endpoint}`;
|
|
}
|
|
|
|
function makeS3Client() {
|
|
const accessKeyId = process.env.DO_ACCESS_KEY_ID;
|
|
|
|
const secretAccessKey = process.env.DO_SECRET_ACCESS_KEY;
|
|
|
|
const region = process.env.DO_REGION;
|
|
|
|
const endpoint = makeEndpoint(process.env.DO_ENDPOINT);
|
|
|
|
return new S3Client({
|
|
region,
|
|
endpoint, // endpoint should be Url e.g. https://blr1.digitaloceanspaces.com)
|
|
credentials: { accessKeyId, secretAccessKey },
|
|
});
|
|
}
|
|
|
|
export default async function getPresignedUrl(url) {
|
|
if (url?.includes('/files/')) {
|
|
return presignedlocalUrl(url);
|
|
} else {
|
|
const client = makeS3Client();
|
|
|
|
const bucket = process.env.DO_SPACE;
|
|
|
|
const key = extractKeyFromUrl(url);
|
|
|
|
const command = new GetObjectCommand({ Bucket: bucket, Key: key });
|
|
// Expires: 160 seconds
|
|
const expiresIn = 160;
|
|
|
|
// presignedGETURL return presignedUrl with expires time
|
|
const presignedGETURL = await presign(client, command, { expiresIn });
|
|
return presignedGETURL;
|
|
}
|
|
}
|
|
|
|
export async function getSignedUrl(request) {
|
|
try {
|
|
const docId = request.params.docId || '';
|
|
const templateId = request.params.templateId || '';
|
|
const url = request.params.url;
|
|
|
|
if (docId || templateId) {
|
|
try {
|
|
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');
|
|
query.notEqualTo('IsArchive', true);
|
|
const res = await query.first({ useMasterKey: true });
|
|
if (!res) return url;
|
|
|
|
const _resDoc = res?.toJSON();
|
|
// Ensure user is authenticated if OTP is required
|
|
if (_resDoc?.IsEnableOTP) {
|
|
const isAuth = await isAuthenticated(request?.user);
|
|
if (!isAuth) {
|
|
throw new Parse.Error(
|
|
Parse.Error.INVALID_SESSION_TOKEN,
|
|
'User is not authenticated.'
|
|
);
|
|
}
|
|
}
|
|
|
|
const presignedUrl = await getPresignedUrl(url);
|
|
return presignedUrl;
|
|
} else {
|
|
return url;
|
|
}
|
|
} catch (err) {
|
|
console.log('Err in presigned url', err);
|
|
throw err;
|
|
}
|
|
} else {
|
|
const isAuth = await isAuthenticated(request?.user);
|
|
if (!isAuth) {
|
|
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
|
} else {
|
|
if (url?.includes('/files/')) {
|
|
return presignedlocalUrl(url);
|
|
} else if (useLocal !== 'true') {
|
|
const presignedUrl = await getPresignedUrl(url);
|
|
return presignedUrl;
|
|
} else {
|
|
return url;
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.log('error in getsignedurl', err);
|
|
const code = err.code || 400;
|
|
const msg = err.message;
|
|
const error = new Parse.Error(code, msg);
|
|
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';
|
|
}
|
|
}
|