remove passwords from all headers, use connection ID and internal auth

This commit is contained in:
MCHost
2025-10-16 00:42:04 -04:00
parent 6479d8bdd8
commit 55768b0ea9
4 changed files with 136 additions and 45 deletions
+2 -1
View File
@@ -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
View File
@@ -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