Skip to content

Commit

Permalink
feat: add ssl with certificate (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
invisal authored Oct 10, 2023
1 parent ccdb275 commit ec84803
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 14 deletions.
10 changes: 9 additions & 1 deletion src/drivers/base/SQLLikeConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,21 @@ export interface MySqlConnectionConfig {
port: number;
}

export type ConnectionSslOption =
| boolean
| {
ca?: string;
cert?: string;
key?: string;
};

export interface PgConnectionConfig {
database: string;
user: string;
password: string;
host: string;
port: number;
ssl?: boolean;
ssl?: ConnectionSslOption;
}

export interface SqliteConnectionConfig {
Expand Down
14 changes: 12 additions & 2 deletions src/main/ipc/ipc_file_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import {
dialog,
SaveDialogSyncOptions,
shell,
OpenDialogOptions,
} from 'electron';
import fs from 'fs';
import saveCsvFile from '../../libs/SaveCSVFile';
import saveExcelFile from '../../libs/SaveExcelFile';
import CommunicateHandler from './../CommunicateHandler';
Expand All @@ -15,15 +17,23 @@ CommunicateHandler.handle(
return dialog.showMessageBoxSync(window, options);
}
return 0;
}
},
)
.handle('show-open-dialog', ([options]: [OpenDialogOptions], { window }) => {
if (window) {
return dialog.showOpenDialog(window, options);
}
})
.handle('read-file', ([fileName]: [string]) => {
return fs.readFileSync(fileName);
})
.handle(
'show-save-dialog',
([options]: [SaveDialogSyncOptions], { window }) => {
if (window) {
return dialog.showSaveDialogSync(window, options);
}
}
},
)
.handle('save-csv-file', ([fileName, records]: [string, object[]]) => {
saveCsvFile(fileName, records);
Expand Down
10 changes: 10 additions & 0 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
MessageBoxSyncOptions,
MenuItemConstructorOptions,
SaveDialogSyncOptions,
OpenDialogOptions,
OpenDialogReturnValue,
} from 'electron';
import {
ProgressInfo,
Expand Down Expand Up @@ -61,6 +63,14 @@ const electronHandler = {
): Promise<string | undefined> =>
ipcRenderer.invoke('show-save-dialog', [options]),

showOpenDialog: (
options: OpenDialogOptions,
): Promise<OpenDialogReturnValue> =>
ipcRenderer.invoke('show-open-dialog', [options]),

readFile: (fileName: string): Promise<Buffer> =>
ipcRenderer.invoke('read-file', [fileName]),

showFileInFolder: (fileName: string) =>
ipcRenderer.invoke('show-item-in-folder', [fileName]),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import styles from './certificate.module.scss';
import {
faCheckCircle,
faCircleMinus,
faCircleXmark,
faFolder,
} from '@fortawesome/free-solid-svg-icons';
import { useCallback } from 'react';
import { ConnectionSslOption } from 'drivers/base/SQLLikeConnection';

interface PickerButtonProps {
label: string;
onChange: (v?: string) => void;
value?: string;
}

interface CertificatePickerProps {
value?: ConnectionSslOption;
onChange: (v: ConnectionSslOption | undefined) => void;
}

function CertificatePickerButton({
label,
onChange,
value,
}: PickerButtonProps) {
const onClick = useCallback(() => {
window.electron.showOpenDialog({}).then((e) => {
if (!e.canceled) {
if (e.filePaths.length === 1) {
window.electron
.readFile(e.filePaths[0])
.then((content) => onChange(new TextDecoder().decode(content)));
}
}
});
}, [onChange]);

const className = [styles.button, value ? styles.provided : undefined]
.filter(Boolean)
.join(' ');

return (
<div className={className}>
<div onClick={onClick}>
<FontAwesomeIcon
icon={value ? faCheckCircle : faCircleMinus}
style={{ opacity: value ? 1 : 0.4, marginRight: 5 }}
/>

<span>{label}</span>
</div>
{value && (
<div
className={`${styles.icon} ${styles.close}`}
onClick={() => {
onChange(undefined);
}}
>
<FontAwesomeIcon
icon={faCircleXmark}
style={{ opacity: 1, marginRight: 5 }}
/>
</div>
)}
{!value && (
<div className={styles.icon} onClick={onClick}>
<FontAwesomeIcon
icon={faFolder}
style={{ opacity: 0.4, marginRight: 5 }}
/>
</div>
)}
</div>
);
}

export default function CertificatePicker({
value,
onChange,
}: CertificatePickerProps) {
const onUpdate = useCallback(
(property: 'ca' | 'cert' | 'key', newValue?: string) => {
let tmp: ConnectionSslOption | undefined;
if (!value) tmp = { [property]: newValue };
else if (value === true) {
tmp = { [property]: newValue };
} else {
tmp = { ...value, [property]: newValue };
}

// Remove all the undefined property
if (typeof tmp === 'object') {
for (const tmpKey of Object.keys(tmp)) {
const key = tmpKey as keyof ConnectionSslOption;
if (!tmp[key]) {
delete tmp[key];
}
}

if (Object.entries(tmp).length === 0) {
onChange(value === true ? true : undefined);
} else onChange(tmp);
} else {
onChange(undefined);
}
},
[value, onChange],
);

const ca = typeof value === 'object' ? value.ca : undefined;
const cert = typeof value === 'object' ? value.cert : undefined;
const key = typeof value === 'object' ? value.key : undefined;
const ssl = !!value;

return (
<div className={styles.container}>
<h3>
<label>
<input
type="checkbox"
checked={ssl}
onClick={() => {
if (!value) onChange(true);
if (value === true) onChange(undefined);
}}
/>
<span>&nbsp;&nbsp;Over SSL</span>
</label>
</h3>
<div className={styles.buttonGroup}>
<CertificatePickerButton
label="CA Certificate"
value={ca}
onChange={(e) => onUpdate('ca', e)}
/>
<CertificatePickerButton
label="Certificate"
value={cert}
onChange={(e) => onUpdate('cert', e)}
/>
<CertificatePickerButton
label="Key"
value={key}
onChange={(e) => onUpdate('key', e)}
/>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,11 @@ export default function PostgreConnectionEditor({
config: ConnectionStoreConfig;
onChange: (value: ConnectionStoreConfig) => void;
}) {
console.log(config);

return (
<Stack vertical>
<RelationalDbConnectionEditor config={config} onChange={onChange} />
<label>
<input
type="checkbox"
checked={config?.ssl}
onChange={(e) =>
onChange({ ...config, ssl: e.currentTarget.checked })
}
/>
&nbsp;Over SSL
</label>
</Stack>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Stack from 'renderer/components/Stack';
import TextField from 'renderer/components/TextField';
import PasswordField from 'renderer/components/PasswordField';
import { useState } from 'react';
import CertificatePicker from './CertificatePicker';

export default function RelationalDbConnectionEditor({
config,
Expand Down Expand Up @@ -52,6 +53,11 @@ export default function RelationalDbConnectionEditor({
</div>
</Stack>

<CertificatePicker
value={config?.ssl}
onChange={(value) => onChange({ ...config, ssl: value })}
/>

<TextField
label="Database"
value={config?.database}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.container {
h3 {
font-size: 1rem;
border-bottom: 1px solid var(--color-border);
padding-bottom: 5px;
margin-bottom: 10px;
}

border-bottom: 1px solid var(--color-border);
padding-bottom: 10px;
}

.buttonGroup {
display: flex;
gap: 10px;
}

.button {
border-radius: 4px;
overflow: hidden;
cursor: pointer;
display: flex;

> div {
background: var(--color-input-border);
padding: 10px;

&:hover {
opacity: 0.8;
}
}
}

.icon {
border-left: 1px solid var(--color-surface);
}

.button.provided {
> div {
background: #27ae60;
color: #fff;
}
}

.close {
background: #e74c3c !important;
color: #fff !important;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ConnectionStoreConfig } from 'drivers/base/SQLLikeConnection';
import MySQLConnectionEditor from './MySQLConnectionEditor';
import PostgreConnectionEditor from './PostgreConnectionEditor copy';
import PostgreConnectionEditor from './PostgreConnectionEditor';

export default function ConnectionConfigEditor({
type,
Expand Down

0 comments on commit ec84803

Please sign in to comment.