-
Notifications
You must be signed in to change notification settings - Fork 750
Expand file tree
/
Copy pathgetSignedUrl.js
More file actions
219 lines (192 loc) · 6.74 KB
/
Copy pathgetSignedUrl.js
File metadata and controls
219 lines (192 loc) · 6.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
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;
}
}
export function normalizeLocalFileUrl(fileUrl) {
if (!fileUrl) return fileUrl;
try {
const parsedFileUrl = new URL(fileUrl);
if (!parsedFileUrl.pathname.includes('/files/')) {
return fileUrl;
}
const configuredServerUrl = process.env.SERVER_URL;
if (!configuredServerUrl) {
return fileUrl;
}
const parsedServerUrl = new URL(configuredServerUrl);
parsedFileUrl.protocol = parsedServerUrl.protocol;
parsedFileUrl.host = parsedServerUrl.host;
return parsedFileUrl.toString();
} catch (err) {
return fileUrl;
}
}
// Function to generate a signed URL with JWT
export function getSignedLocalUrl(fileUrl, expirationTimeInSeconds) {
const secretKey = process.env.MASTER_KEY;
const exp = expirationTimeInSeconds || 200;
try {
const normalizedFileUrl = normalizeLocalFileUrl(fileUrl);
// Create the payload with the file URL and expiration time
const payload = {
fileUrl: normalizedFileUrl,
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 `${normalizedFileUrl}?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 = normalizeLocalFileUrl(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';
}
}