mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-28 15:14:06 +00:00
Fix/client cert passphrase issues (#5898)
* fix: added interpolation, warning and syntax highlight for passphrase input Changes: 1) When users add plain text in passphrase, warning message will be shown. 2) Passphrase will be interpolated from environment 3) Syntax highlighting for variables added. Closes #2685 * fix: global environment variables interpolation in cert passphrase implemented. * refactor: indentation refactoring
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
import React from 'react';
|
||||
import { IconCertificate, IconTrash, IconWorld } from '@tabler/icons';
|
||||
import { useFormik } from 'formik';
|
||||
import { uuid } from 'utils/common';
|
||||
import * as Yup from 'yup';
|
||||
import { IconEye, IconEyeOff } from '@tabler/icons';
|
||||
import { useState } from 'react';
|
||||
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { useRef } from 'react';
|
||||
import path from 'utils/common/path';
|
||||
import SensitiveFieldWarning from 'components/SensitiveFieldWarning/index';
|
||||
import SingleLineEditor from 'components/SingleLineEditor/index';
|
||||
import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField/index';
|
||||
import { useTheme } from 'styled-components';
|
||||
|
||||
const ClientCertSettings = ({ root, clientCertConfig, onUpdate, onRemove }) => {
|
||||
const ClientCertSettings = ({ collection, clientCertConfig, onUpdate, onRemove }) => {
|
||||
const certFilePathInputRef = useRef();
|
||||
const keyFilePathInputRef = useRef();
|
||||
const pfxFilePathInputRef = useRef();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
@@ -68,10 +69,13 @@ const ClientCertSettings = ({ root, clientCertConfig, onUpdate, onRemove }) => {
|
||||
}
|
||||
});
|
||||
|
||||
const { isSensitive } = useDetectSensitiveField(collection);
|
||||
const { showWarning, warningMessage } = isSensitive(formik.values.passphrase);
|
||||
|
||||
const getFile = (e) => {
|
||||
const filePath = window?.ipcRenderer?.getFilePath(e?.files?.[0]);
|
||||
if (filePath) {
|
||||
let relativePath = path.relative(root, filePath);
|
||||
let relativePath = path.relative(collection.pathname, filePath);
|
||||
formik.setFieldValue(e.name, relativePath);
|
||||
}
|
||||
};
|
||||
@@ -82,8 +86,6 @@ const ClientCertSettings = ({ root, clientCertConfig, onUpdate, onRemove }) => {
|
||||
pfxFilePathInputRef.current.value = '';
|
||||
};
|
||||
|
||||
const [passwordVisible, setPasswordVisible] = useState(false);
|
||||
|
||||
const handleTypeChange = (e) => {
|
||||
formik.setFieldValue('type', e.target.value);
|
||||
if (e.target.value === 'cert') {
|
||||
@@ -314,21 +316,14 @@ const ClientCertSettings = ({ root, clientCertConfig, onUpdate, onRemove }) => {
|
||||
Passphrase
|
||||
</label>
|
||||
<div className="textbox flex flex-row items-center w-[300px] h-[1.70rem] relative">
|
||||
<input
|
||||
id="passphrase"
|
||||
type={passwordVisible ? 'text' : 'password'}
|
||||
name="passphrase"
|
||||
className="outline-none w-64 bg-transparent"
|
||||
onChange={formik.handleChange}
|
||||
<SingleLineEditor
|
||||
value={formik.values.passphrase || ''}
|
||||
theme={storedTheme}
|
||||
onChange={(val) => formik.setFieldValue('passphrase', val)}
|
||||
collection={collection}
|
||||
isSecret={true}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm absolute right-0 l"
|
||||
onClick={() => setPasswordVisible(!passwordVisible)}
|
||||
>
|
||||
{passwordVisible ? <IconEyeOff size={18} strokeWidth={1.5} /> : <IconEye size={18} strokeWidth={1.5} />}
|
||||
</button>
|
||||
{showWarning && <SensitiveFieldWarning fieldName="basic-password" warningMessage={warningMessage} />}
|
||||
</div>
|
||||
{formik.touched.passphrase && formik.errors.passphrase ? (
|
||||
<div className="ml-1 text-red-500">{formik.errors.passphrase}</div>
|
||||
|
||||
@@ -120,7 +120,7 @@ const CollectionSettings = ({ collection }) => {
|
||||
case 'clientCert': {
|
||||
return (
|
||||
<ClientCertSettings
|
||||
root={collection.pathname}
|
||||
collection={collection}
|
||||
clientCertConfig={clientCertConfig}
|
||||
onUpdate={onClientCertSettingsUpdate}
|
||||
onRemove={onClientCertSettingsRemove}
|
||||
|
||||
@@ -187,7 +187,7 @@ class SingleLineEditor extends Component {
|
||||
*/
|
||||
secretEye = (isSecret) => {
|
||||
return isSecret === true ? (
|
||||
<button className="mx-2" onClick={() => this.toggleVisibleSecret()}>
|
||||
<button type="button" className="mx-2" onClick={() => this.toggleVisibleSecret()}>
|
||||
{this.state.maskInput === true ? (
|
||||
<IconEyeOff size={18} strokeWidth={2} />
|
||||
) : (
|
||||
|
||||
@@ -974,80 +974,82 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
||||
|
||||
ipcMain.handle('renderer:fetch-oauth2-credentials', async (event, { itemUid, request, collection }) => {
|
||||
try {
|
||||
if (request.oauth2) {
|
||||
let requestCopy = _.cloneDeep(request);
|
||||
const { uid: collectionUid, pathname: collectionPath, runtimeVariables, environments = [], activeEnvironmentUid } = collection;
|
||||
const environment = _.find(environments, (e) => e.uid === activeEnvironmentUid);
|
||||
const envVars = getEnvVars(environment);
|
||||
const processEnvVars = getProcessEnvVars(collectionUid);
|
||||
const partialItem = { uid: itemUid };
|
||||
const requestTreePath = getTreePathFromCollectionToItem(collection, partialItem);
|
||||
mergeVars(collection, requestCopy, requestTreePath);
|
||||
if (request.oauth2) {
|
||||
let requestCopy = _.cloneDeep(request);
|
||||
const { uid: collectionUid, pathname: collectionPath, runtimeVariables, environments = [], activeEnvironmentUid } = collection;
|
||||
const environment = _.find(environments, (e) => e.uid === activeEnvironmentUid);
|
||||
const envVars = getEnvVars(environment);
|
||||
const processEnvVars = getProcessEnvVars(collectionUid);
|
||||
const partialItem = { uid: itemUid };
|
||||
const requestTreePath = getTreePathFromCollectionToItem(collection, partialItem);
|
||||
mergeVars(collection, requestCopy, requestTreePath);
|
||||
const globalEnvironmentVariables = collection.globalEnvironmentVariables;
|
||||
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
const certsAndProxyConfig = await getCertsAndProxyConfig({
|
||||
collectionUid,
|
||||
request: requestCopy,
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath
|
||||
});
|
||||
const { oauth2: { grantType }} = requestCopy || {};
|
||||
|
||||
const handleOAuth2Response = (response) => {
|
||||
if (response.error && !response.debugInfo) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
switch (grantType) {
|
||||
case 'authorization_code':
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
return await getOAuth2TokenUsingAuthorizationCode({
|
||||
request: requestCopy,
|
||||
collectionUid,
|
||||
forceFetch: true,
|
||||
certsAndProxyConfig
|
||||
}).then(handleOAuth2Response);
|
||||
|
||||
case 'client_credentials':
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
return await getOAuth2TokenUsingClientCredentials({
|
||||
request: requestCopy,
|
||||
collectionUid,
|
||||
forceFetch: true,
|
||||
certsAndProxyConfig
|
||||
}).then(handleOAuth2Response);
|
||||
|
||||
case 'password':
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
return await getOAuth2TokenUsingPasswordCredentials({
|
||||
request: requestCopy,
|
||||
collectionUid,
|
||||
forceFetch: true,
|
||||
certsAndProxyConfig
|
||||
}).then(handleOAuth2Response);
|
||||
|
||||
case 'implicit':
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
return await getOAuth2TokenUsingImplicitGrant({
|
||||
request: requestCopy,
|
||||
collectionUid,
|
||||
forceFetch: true
|
||||
}).then(handleOAuth2Response);
|
||||
|
||||
default:
|
||||
return {
|
||||
error: `Unsupported grant type: ${grantType}`,
|
||||
credentials: null,
|
||||
url: null,
|
||||
collectionUid,
|
||||
credentialsId: null
|
||||
};
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
const certsAndProxyConfig = await getCertsAndProxyConfig({
|
||||
collectionUid,
|
||||
request: requestCopy,
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath,
|
||||
globalEnvironmentVariables
|
||||
});
|
||||
const { oauth2: { grantType } } = requestCopy || {};
|
||||
|
||||
const handleOAuth2Response = (response) => {
|
||||
if (response.error && !response.debugInfo) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
switch (grantType) {
|
||||
case 'authorization_code':
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
return await getOAuth2TokenUsingAuthorizationCode({
|
||||
request: requestCopy,
|
||||
collectionUid,
|
||||
forceFetch: true,
|
||||
certsAndProxyConfig
|
||||
}).then(handleOAuth2Response);
|
||||
|
||||
case 'client_credentials':
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
return await getOAuth2TokenUsingClientCredentials({
|
||||
request: requestCopy,
|
||||
collectionUid,
|
||||
forceFetch: true,
|
||||
certsAndProxyConfig
|
||||
}).then(handleOAuth2Response);
|
||||
|
||||
case 'password':
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
return await getOAuth2TokenUsingPasswordCredentials({
|
||||
request: requestCopy,
|
||||
collectionUid,
|
||||
forceFetch: true,
|
||||
certsAndProxyConfig
|
||||
}).then(handleOAuth2Response);
|
||||
|
||||
case 'implicit':
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
return await getOAuth2TokenUsingImplicitGrant({
|
||||
request: requestCopy,
|
||||
collectionUid,
|
||||
forceFetch: true
|
||||
}).then(handleOAuth2Response);
|
||||
|
||||
default:
|
||||
return {
|
||||
error: `Unsupported grant type: ${grantType}`,
|
||||
credentials: null,
|
||||
url: null,
|
||||
collectionUid,
|
||||
credentialsId: null
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
@@ -1105,29 +1107,31 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
||||
|
||||
ipcMain.handle('renderer:refresh-oauth2-credentials', async (event, { itemUid, request, collection }) => {
|
||||
try {
|
||||
if (request.oauth2) {
|
||||
let requestCopy = _.cloneDeep(request);
|
||||
const { uid: collectionUid, pathname: collectionPath, runtimeVariables, environments = [], activeEnvironmentUid } = collection;
|
||||
const environment = _.find(environments, (e) => e.uid === activeEnvironmentUid);
|
||||
const envVars = getEnvVars(environment);
|
||||
const processEnvVars = getProcessEnvVars(collectionUid);
|
||||
const partialItem = { uid: itemUid };
|
||||
const requestTreePath = getTreePathFromCollectionToItem(collection, partialItem);
|
||||
mergeVars(collection, requestCopy, requestTreePath);
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
if (request.oauth2) {
|
||||
let requestCopy = _.cloneDeep(request);
|
||||
const { uid: collectionUid, pathname: collectionPath, runtimeVariables, environments = [], activeEnvironmentUid } = collection;
|
||||
const environment = _.find(environments, (e) => e.uid === activeEnvironmentUid);
|
||||
const envVars = getEnvVars(environment);
|
||||
const processEnvVars = getProcessEnvVars(collectionUid);
|
||||
const partialItem = { uid: itemUid };
|
||||
const requestTreePath = getTreePathFromCollectionToItem(collection, partialItem);
|
||||
mergeVars(collection, requestCopy, requestTreePath);
|
||||
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
|
||||
const globalEnvironmentVariables = collection.globalEnvironmentVariables;
|
||||
|
||||
const certsAndProxyConfig = await getCertsAndProxyConfig({
|
||||
collectionUid,
|
||||
request: requestCopy,
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath
|
||||
});
|
||||
|
||||
let { credentials, url, credentialsId, debugInfo } = await refreshOauth2Token({ requestCopy, collectionUid, certsAndProxyConfig });
|
||||
return { credentials, url, collectionUid, credentialsId, debugInfo };
|
||||
}
|
||||
const certsAndProxyConfig = await getCertsAndProxyConfig({
|
||||
collectionUid,
|
||||
request: requestCopy,
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath,
|
||||
globalEnvironmentVariables
|
||||
});
|
||||
|
||||
let { credentials, url, credentialsId, debugInfo } = await refreshOauth2Token({ requestCopy, collectionUid, certsAndProxyConfig });
|
||||
return { credentials, url, collectionUid, credentialsId, debugInfo };
|
||||
}
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ const getCertsAndProxyConfig = async ({
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath
|
||||
collectionPath,
|
||||
globalEnvironmentVariables
|
||||
}) => {
|
||||
/**
|
||||
* @see https://github.com/usebruno/bruno/issues/211 set keepAlive to true, this should fix socket hang up errors
|
||||
@@ -41,6 +42,7 @@ const getCertsAndProxyConfig = async ({
|
||||
|
||||
const brunoConfig = getBrunoConfig(collectionUid);
|
||||
const interpolationOptions = {
|
||||
globalEnvironmentVariables,
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars
|
||||
|
||||
@@ -171,7 +171,8 @@ const registerGrpcEventHandlers = (window) => {
|
||||
envVars: preparedRequest.envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars: preparedRequest.processEnvVars,
|
||||
collectionPath: collection.pathname
|
||||
collectionPath: collection.pathname,
|
||||
globalEnvironmentVariables: collection.globalEnvironmentVariables
|
||||
});
|
||||
|
||||
|
||||
@@ -302,7 +303,8 @@ const registerGrpcEventHandlers = (window) => {
|
||||
envVars: preparedRequest.envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars: preparedRequest.processEnvVars,
|
||||
collectionPath: collection.pathname
|
||||
collectionPath: collection.pathname,
|
||||
globalEnvironmentVariables: collection.globalEnvironmentVariables
|
||||
});
|
||||
|
||||
// Extract certificate information from the config
|
||||
|
||||
@@ -75,7 +75,8 @@ const configureRequest = async (
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath
|
||||
collectionPath,
|
||||
globalEnvironmentVariables
|
||||
) => {
|
||||
const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/;
|
||||
if (!protocolRegex.test(request.url)) {
|
||||
@@ -88,7 +89,8 @@ const configureRequest = async (
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath
|
||||
collectionPath,
|
||||
globalEnvironmentVariables
|
||||
});
|
||||
|
||||
// Get followRedirects setting, default to true for backward compatibility
|
||||
@@ -316,7 +318,8 @@ const fetchGqlSchemaHandler = async (event, endpoint, environment, _request, col
|
||||
envVars,
|
||||
collection.runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath
|
||||
collectionPath,
|
||||
collection.globalEnvironmentVariables
|
||||
);
|
||||
|
||||
const response = await axiosInstance(request);
|
||||
@@ -642,7 +645,8 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath
|
||||
collectionPath,
|
||||
collection.globalEnvironmentVariables
|
||||
);
|
||||
|
||||
const { data: requestData, dataBuffer: requestDataBuffer } = parseDataFromRequest(request);
|
||||
@@ -1165,7 +1169,8 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
processEnvVars,
|
||||
collectionPath
|
||||
collectionPath,
|
||||
collection.globalEnvironmentVariables
|
||||
);
|
||||
|
||||
if (request?.oauth2Credentials) {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
const { forOwn, cloneDeep } = require('lodash');
|
||||
const { interpolate } = require('@usebruno/common');
|
||||
|
||||
const interpolateString = (str, { envVars, runtimeVariables, processEnvVars }) => {
|
||||
const interpolateString = (str, { globalEnvironmentVariables, envVars, runtimeVariables, processEnvVars }) => {
|
||||
if (!str || !str.length || typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
|
||||
processEnvVars = processEnvVars || {};
|
||||
runtimeVariables = runtimeVariables || {};
|
||||
globalEnvironmentVariables = globalEnvironmentVariables || {};
|
||||
|
||||
// we clone envVars because we don't want to modify the original object
|
||||
envVars = envVars ? cloneDeep(envVars) : {};
|
||||
@@ -26,6 +27,7 @@ const interpolateString = (str, { envVars, runtimeVariables, processEnvVars }) =
|
||||
|
||||
// runtimeVariables take precedence over envVars
|
||||
const combinedVars = {
|
||||
...globalEnvironmentVariables,
|
||||
...envVars,
|
||||
...runtimeVariables,
|
||||
process: {
|
||||
|
||||
Reference in New Issue
Block a user