Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add custom CA Certificate preference #1155

Merged
merged 1 commit into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 79 additions & 1 deletion packages/bruno-app/src/components/Preferences/General/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import React from 'react';
import React, { useRef } from 'react';
import get from 'lodash/get';
import { useFormik } from 'formik';
import { useSelector, useDispatch } from 'react-redux';
import { savePreferences } from 'providers/ReduxStore/slices/app';
import StyledWrapper from './StyledWrapper';
import * as Yup from 'yup';
import toast from 'react-hot-toast';
import path from 'path';
import slash from 'utils/common/slash';
import { IconTrash } from '@tabler/icons';

const General = ({ close }) => {
const preferences = useSelector((state) => state.app.preferences);
const dispatch = useDispatch();
const inputFileCaCertificateRef = useRef();

const preferencesSchema = Yup.object().shape({
sslVerification: Yup.boolean(),
customCaCertificate: Yup.object({
enabled: Yup.boolean(),
filePath: Yup.string().nullable()
}),
storeCookies: Yup.boolean(),
sendCookies: Yup.boolean(),
timeout: Yup.mixed()
Expand All @@ -31,6 +39,10 @@ const General = ({ close }) => {
const formik = useFormik({
initialValues: {
sslVerification: preferences.request.sslVerification,
customCaCertificate: {
enabled: get(preferences, 'request.customCaCertificate.enabled', false),
filePath: get(preferences, 'request.customCaCertificate.filePath', null)
},
timeout: preferences.request.timeout,
storeCookies: get(preferences, 'request.storeCookies', true),
sendCookies: get(preferences, 'request.sendCookies', true)
Expand All @@ -52,6 +64,10 @@ const General = ({ close }) => {
...preferences,
request: {
sslVerification: newPreferences.sslVerification,
customCaCertificate: {
enabled: newPreferences.customCaCertificate.enabled,
filePath: newPreferences.customCaCertificate.filePath
},
timeout: newPreferences.timeout,
storeCookies: newPreferences.storeCookies,
sendCookies: newPreferences.sendCookies
Expand All @@ -64,6 +80,14 @@ const General = ({ close }) => {
.catch((err) => console.log(err) && toast.error('Failed to update preferences'));
};

const addCaCertificate = (e) => {
formik.setFieldValue('customCaCertificate.filePath', e.target.files[0]?.path);
};

const deleteCaCertificate = () => {
formik.setFieldValue('customCaCertificate.filePath', null);
};

return (
<StyledWrapper>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
Expand All @@ -80,6 +104,60 @@ const General = ({ close }) => {
SSL/TLS Certificate Verification
</label>
</div>
<div className="flex items-center mt-2">
<input
id="customCaCertificateEnabled"
type="checkbox"
name="customCaCertificate.enabled"
checked={formik.values.customCaCertificate.enabled}
onChange={formik.handleChange}
className="mousetrap mr-0"
/>
<label className="block ml-2 select-none" htmlFor="customCaCertificateEnabled">
Use custom CA Certificate
</label>
</div>
{formik.values.customCaCertificate.filePath ? (
<div
className={`flex items-center mt-2 pl-6 ${formik.values.customCaCertificate.enabled ? '' : 'opacity-25'}`}
>
<span className="flex items-center border px-2 rounded-md">
{path.basename(slash(formik.values.customCaCertificate.filePath))}
<button
type="button"
tabIndex="-1"
className="pl-1"
disabled={formik.values.customCaCertificate.enabled ? false : true}
onClick={deleteCaCertificate}
>
<IconTrash strokeWidth={1.5} size={14} />
</button>
</span>
</div>
) : (
<div
className={`flex items-center mt-2 pl-6 ${formik.values.customCaCertificate.enabled ? '' : 'opacity-25'}`}
>
<button
type="button"
tabIndex="-1"
className="flex items-center border px-2 rounded-md"
disabled={formik.values.customCaCertificate.enabled ? false : true}
onClick={() => inputFileCaCertificateRef.current.click()}
>
select file
<input
id="caCertFilePath"
type="file"
name="customCaCertificate.filePath"
className="hidden"
ref={inputFileCaCertificateRef}
disabled={formik.values.customCaCertificate.enabled ? false : true}
onChange={addCaCertificate}
/>
</button>
</div>
)}
<div className="flex items-center mt-2">
<input
id="storeCookies"
Expand Down
4 changes: 4 additions & 0 deletions packages/bruno-app/src/providers/ReduxStore/slices/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const initialState = {
preferences: {
request: {
sslVerification: true,
customCaCertificate: {
enabled: false,
filePath: null
},
timeout: 0
},
font: {
Expand Down
13 changes: 12 additions & 1 deletion packages/bruno-electron/src/ipc/network/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,22 @@ const configureRequest = async (
request.url = `http://${request.url}`;
}

const httpsAgentRequestFields = {};
/**
* @see https://github.com/usebruno/bruno/issues/211 set keepAlive to true, this should fix socket hang up errors
* @see https://github.com/nodejs/node/pull/43522 keepAlive was changed to true globally on Node v19+
*/
const httpsAgentRequestFields = { keepAlive: true };
if (!preferencesUtil.shouldVerifyTls()) {
httpsAgentRequestFields['rejectUnauthorized'] = false;
}

if (preferencesUtil.shouldUseCustomCaCertificate()) {
const caCertFilePath = preferencesUtil.getCustomCaCertificateFilePath();
if (caCertFilePath) {
httpsAgentRequestFields['ca'] = fs.readFileSync(caCertFilePath);
}
}

const brunoConfig = getBrunoConfig(collectionUid);
const interpolationOptions = {
envVars,
Expand Down
14 changes: 14 additions & 0 deletions packages/bruno-electron/src/store/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const { get } = require('lodash');
const defaultPreferences = {
request: {
sslVerification: true,
customCaCertificate: {
enabled: false,
filePath: null
},
storeCookies: true,
sendCookies: true,
timeout: 0
Expand All @@ -35,6 +39,10 @@ const defaultPreferences = {
const preferencesSchema = Yup.object().shape({
request: Yup.object().shape({
sslVerification: Yup.boolean(),
customCaCertificate: Yup.object({
enabled: Yup.boolean(),
filePath: Yup.string().nullable()
}),
storeCookies: Yup.boolean(),
sendCookies: Yup.boolean(),
timeout: Yup.number()
Expand Down Expand Up @@ -100,6 +108,12 @@ const preferencesUtil = {
shouldVerifyTls: () => {
return get(getPreferences(), 'request.sslVerification', true);
},
shouldUseCustomCaCertificate: () => {
return get(getPreferences(), 'request.customCaCertificate.enabled', false);
},
getCustomCaCertificateFilePath: () => {
return get(getPreferences(), 'request.customCaCertificate.filePath', null);
},
getRequestTimeout: () => {
return get(getPreferences(), 'request.timeout', 0);
},
Expand Down