remove passwords from all headers, use connection ID and internal auth
This commit is contained in:
+2
-1
@@ -23,7 +23,7 @@ const fetchConnectionDetails = async (connectionId) => {
|
||||
};
|
||||
const updatePreview = async() => {
|
||||
// Make sure the file is viewable
|
||||
const extInfo = getFileExtInfo(path);
|
||||
const extInfo = getFileExtInfo(path, fileStats.size);
|
||||
if (!extInfo.isViewable) {
|
||||
return setStatus(`Error: File isn't viewable!`, true);
|
||||
}
|
||||
@@ -464,6 +464,7 @@ window.addEventListener('load', async() => {
|
||||
if (!connectionDetails) {
|
||||
return;
|
||||
}
|
||||
activeServerConnectionId = conId;
|
||||
activeConnection = {
|
||||
name: `${connectionDetails.username}@${connectionDetails.host}`,
|
||||
host: connectionDetails.host,
|
||||
|
||||
+113
-29
@@ -1,4 +1,3 @@
|
||||
|
||||
const elProgressBar = $('#progressBar');
|
||||
const elStatusBar = $('#statusBar');
|
||||
const isElectron = window && window.process && window.process.type;
|
||||
@@ -16,6 +15,102 @@ let connections = JSON.parse(window.localStorage.getItem('connections')) || {};
|
||||
let activeConnection = null;
|
||||
/** The ID of the current active connection */
|
||||
let activeConnectionId = null;
|
||||
/** The active server connection ID */
|
||||
let activeServerConnectionId = null;
|
||||
|
||||
// Load active connection ID if saved
|
||||
activeConnectionId = window.localStorage.getItem('activeConnectionId');
|
||||
|
||||
|
||||
let urlConnectionId = null;
|
||||
if (window.location.pathname.startsWith('/connect/')) {
|
||||
const match = window.location.pathname.match(/\/connect\/([a-f0-9]{32})/);
|
||||
if (match) {
|
||||
urlConnectionId = match[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the bottom status bar.
|
||||
* @param {string} html The status text
|
||||
* @param {boolean} isError If `true`, turns the status red
|
||||
* @param {number|null} progress A 0-100 whole number to be used for the progress bar, or `null` to hide it
|
||||
* @returns {boolean} The negation of `isError`
|
||||
*/
|
||||
const setStatus = (html, isError = false, progress = null) => {
|
||||
elStatusBar.innerHTML = html;
|
||||
elStatusBar.classList.toggle('error', isError);
|
||||
elProgressBar.classList.remove('visible');
|
||||
if (progress !== null) {
|
||||
elProgressBar.classList.add('visible');
|
||||
if (progress >= 0 && progress <= 100)
|
||||
elProgressBar.value = progress;
|
||||
else
|
||||
elProgressBar.removeAttribute('value');
|
||||
}
|
||||
return !isError;
|
||||
}
|
||||
|
||||
// Wrap initialization in async function to handle awaits
|
||||
const initPromise = (async () => {
|
||||
if (urlConnectionId) {
|
||||
try {
|
||||
const res = await axios.get(`${httpProtocol}://${apiHost}/api/connect/${urlConnectionId}`);
|
||||
if (res.data.success) {
|
||||
activeConnection = res.data.connection;
|
||||
activeServerConnectionId = urlConnectionId;
|
||||
activeConnectionId = `shared_${urlConnectionId}`;
|
||||
} else {
|
||||
setStatus(`Failed to load shared connection: ${res.data.error}`, true);
|
||||
}
|
||||
} catch (error) {
|
||||
setStatus(`Error loading shared connection: ${error.message}`, true);
|
||||
}
|
||||
} else {
|
||||
if (activeConnectionId && connections[activeConnectionId]) {
|
||||
activeConnection = connections[activeConnectionId];
|
||||
} else if (Object.keys(connections).length > 0) {
|
||||
activeConnectionId = Object.keys(connections)[0];
|
||||
activeConnection = connections[activeConnectionId];
|
||||
window.localStorage.setItem('activeConnectionId', activeConnectionId);
|
||||
} else {
|
||||
activeConnection = null;
|
||||
activeConnectionId = null;
|
||||
}
|
||||
|
||||
activeServerConnectionId = window.localStorage.getItem(`serverConnectionId_${activeConnectionId}`) || window.localStorage.getItem('activeServerConnectionId');
|
||||
if (!activeServerConnectionId && activeConnection) {
|
||||
if (!activeConnection.password && !activeConnection.privateKey) {
|
||||
setStatus('Connection requires password or key, but none provided', true);
|
||||
activeConnection = null;
|
||||
activeConnectionId = null;
|
||||
} else {
|
||||
try {
|
||||
const response = await axios.post(`${httpProtocol}://${apiHost}/auto-connection`, {
|
||||
host: activeConnection.host,
|
||||
port: activeConnection.port,
|
||||
username: activeConnection.username,
|
||||
password: activeConnection.password,
|
||||
privateKey: activeConnection.privateKey
|
||||
});
|
||||
if (response.data.success) {
|
||||
activeServerConnectionId = response.data.connectionId;
|
||||
window.localStorage.setItem(`serverConnectionId_${activeConnectionId}`, activeServerConnectionId);
|
||||
window.localStorage.setItem('activeServerConnectionId', activeServerConnectionId);
|
||||
} else {
|
||||
setStatus(`Failed to create server connection: ${response.data.error}`, true);
|
||||
activeConnection = null;
|
||||
activeConnectionId = null;
|
||||
}
|
||||
} catch (error) {
|
||||
setStatus(`Error creating server connection: ${error.message}`, true);
|
||||
activeConnection = null;
|
||||
activeConnectionId = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* Checks if two HTML elements overlap
|
||||
@@ -210,15 +305,13 @@ const getIsMobileDevice = () => {
|
||||
* Returns an object of headers for API requests that interface with the current active server
|
||||
*/
|
||||
const getHeaders = () => {
|
||||
if (!activeServerConnectionId) {
|
||||
console.warn('No active server connection; cannot generate headers');
|
||||
return {};
|
||||
}
|
||||
const headers = {
|
||||
'sftp-host': activeConnection.host,
|
||||
'sftp-port': activeConnection.port,
|
||||
'sftp-username': activeConnection.username
|
||||
'sftp-connection-id': activeServerConnectionId
|
||||
};
|
||||
if (activeConnection.password)
|
||||
headers['sftp-password'] = encodeURIComponent(activeConnection.password);
|
||||
if (activeConnection.key)
|
||||
headers['sftp-key'] = encodeURIComponent(activeConnection.key);
|
||||
return headers;
|
||||
}
|
||||
|
||||
@@ -233,6 +326,18 @@ const api = {
|
||||
* @returns {object} An object representing the response data or error info
|
||||
*/
|
||||
request: async (method, url, params, body = null, onProgress = () => {}, responseType = 'json') => {
|
||||
try {
|
||||
await initPromise;
|
||||
} catch (error) {
|
||||
console.error('Initialization failed:', error);
|
||||
}
|
||||
if (!activeServerConnectionId) {
|
||||
setStatus('No active connection selected. Please select a connection from the menu.', true);
|
||||
return {
|
||||
success: false,
|
||||
error: 'No active connection selected'
|
||||
};
|
||||
}
|
||||
url = `${httpProtocol}://${apiHost}/api/sftp/${url}`;
|
||||
try {
|
||||
const opts = {
|
||||
@@ -272,27 +377,6 @@ const api = {
|
||||
delete: (url, params) => api.request('delete', url, params)
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the bottom status bar.
|
||||
* @param {string} html The status text
|
||||
* @param {boolean} isError If `true`, turns the status red
|
||||
* @param {number|null} progress A 0-100 whole number to be used for the progress bar, or `null` to hide it
|
||||
* @returns {boolean} The negation of `isError`
|
||||
*/
|
||||
const setStatus = (html, isError = false, progress = null) => {
|
||||
elStatusBar.innerHTML = html;
|
||||
elStatusBar.classList.toggle('error', isError);
|
||||
elProgressBar.classList.remove('visible');
|
||||
if (progress !== null) {
|
||||
elProgressBar.classList.add('visible');
|
||||
if (progress >= 0 && progress <= 100)
|
||||
elProgressBar.value = progress;
|
||||
else
|
||||
elProgressBar.removeAttribute('value');
|
||||
}
|
||||
return !isError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves with a download URL for a single file, or `false` if an error occurred.
|
||||
* @param {string} path The file path
|
||||
|
||||
Reference in New Issue
Block a user