Skip to content
Snippets Groups Projects
Unverified Commit 8e077e02 authored by Michael Clark's avatar Michael Clark Committed by GitHub
Browse files

:electron: Desktop app to work with self signed certificates (#3308)

* solves the problem but creates a vulnerability

* sake...

* working but need to specify rootca.pem

* works

* being flexible on the cert names, as long as its a crt or pem

* remove console logs

* initial setup for adding cert

* caps

* comments

* fix ts strict

* rewrote it

* release notes

* remove unneeded

* https no polyfill

* removing the cert reference if it is not found

* moving full stop
parent ae608f0c
No related branches found
No related tags found
No related merge requests found
...@@ -8,11 +8,13 @@ import { ...@@ -8,11 +8,13 @@ import {
} from 'loot-core/src/shared/environment'; } from 'loot-core/src/shared/environment';
import { useActions } from '../../hooks/useActions'; import { useActions } from '../../hooks/useActions';
import { useGlobalPref } from '../../hooks/useGlobalPref';
import { useNavigate } from '../../hooks/useNavigate'; import { useNavigate } from '../../hooks/useNavigate';
import { useSetThemeColor } from '../../hooks/useSetThemeColor'; import { useSetThemeColor } from '../../hooks/useSetThemeColor';
import { theme } from '../../style'; import { theme } from '../../style';
import { Button, ButtonWithLoading } from '../common/Button2'; import { Button, ButtonWithLoading } from '../common/Button2';
import { BigInput } from '../common/Input'; import { BigInput } from '../common/Input';
import { Link } from '../common/Link';
import { Text } from '../common/Text'; import { Text } from '../common/Text';
import { View } from '../common/View'; import { View } from '../common/View';
import { useServerURL, useSetServerURL } from '../ServerContext'; import { useServerURL, useSetServerURL } from '../ServerContext';
...@@ -32,6 +34,9 @@ export function ConfigServer() { ...@@ -32,6 +34,9 @@ export function ConfigServer() {
}, [currentUrl]); }, [currentUrl]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [_serverSelfSignedCert, setServerSelfSignedCert] = useGlobalPref(
'serverSelfSignedCert',
);
function getErrorMessage(error: string) { function getErrorMessage(error: string) {
switch (error) { switch (error) {
...@@ -83,6 +88,23 @@ export function ConfigServer() { ...@@ -83,6 +88,23 @@ export function ConfigServer() {
setUrl(window.location.origin); setUrl(window.location.origin);
} }
async function onSelectSelfSignedCertificate() {
const selfSignedCertificateLocation = await window.Actual?.openFileDialog({
properties: ['openFile'],
filters: [
{
name: 'Self Signed Certificate',
extensions: ['crt', 'pem'],
},
],
});
if (selfSignedCertificateLocation) {
setServerSelfSignedCert(selfSignedCertificateLocation[0]);
globalThis.window.Actual.relaunch(); // relaunch to use the certificate
}
}
async function onSkip() { async function onSkip() {
await setServerUrl(null); await setServerUrl(null);
await loggedIn(); await loggedIn();
...@@ -121,16 +143,43 @@ export function ConfigServer() { ...@@ -121,16 +143,43 @@ export function ConfigServer() {
</Text> </Text>
{error && ( {error && (
<Text <>
style={{ <Text
marginTop: 20, style={{
color: theme.errorText, marginTop: 20,
borderRadius: 4, color: theme.errorText,
fontSize: 15, borderRadius: 4,
}} fontSize: 15,
> }}
{getErrorMessage(error)} >
</Text> {getErrorMessage(error)}
</Text>
{isElectron() && (
<View
style={{ display: 'flex', flexDirection: 'row', marginTop: 20 }}
>
<Text
style={{
color: theme.errorText,
borderRadius: 4,
fontSize: 15,
}}
>
<Trans>
If the server is using a self-signed certificate{' '}
<Link
variant="text"
style={{ fontSize: 15 }}
onClick={onSelectSelfSignedCertificate}
>
select it here
</Link>
.
</Trans>
</Text>
</View>
)}
</>
)} )}
<View style={{ display: 'flex', flexDirection: 'row', marginTop: 30 }}> <View style={{ display: 'flex', flexDirection: 'row', marginTop: 30 }}>
......
// // @ts-strict-ignore // @ts-strict-ignore
import nodeFetch from 'node-fetch'; import nodeFetch from 'node-fetch';
export const fetch = (input: RequestInfo | URL, options?: RequestInit) => { export const fetch = (input: RequestInfo | URL, options?: RequestInit) => {
......
// @ts-strict-ignore // @ts-strict-ignore
import './polyfills'; import './polyfills';
import https from 'https';
import tls from 'tls';
import * as injectAPI from '@actual-app/api/injected'; import * as injectAPI from '@actual-app/api/injected';
import * as CRDT from '@actual-app/crdt'; import * as CRDT from '@actual-app/crdt';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
...@@ -1253,6 +1256,12 @@ handlers['save-global-prefs'] = async function (prefs) { ...@@ -1253,6 +1256,12 @@ handlers['save-global-prefs'] = async function (prefs) {
if ('theme' in prefs) { if ('theme' in prefs) {
await asyncStorage.setItem('theme', prefs.theme); await asyncStorage.setItem('theme', prefs.theme);
} }
if ('serverSelfSignedCert' in prefs) {
await asyncStorage.setItem(
'server-self-signed-cert',
prefs.serverSelfSignedCert,
);
}
return 'ok'; return 'ok';
}; };
...@@ -2126,6 +2135,23 @@ export async function initApp(isDev, socketName) { ...@@ -2126,6 +2135,23 @@ export async function initApp(isDev, socketName) {
} }
} }
const selfSignedCertPath = await asyncStorage.getItem(
'server-self-signed-cert',
);
if (selfSignedCertPath) {
try {
const selfSignedCert = await fs.readFile(selfSignedCertPath);
https.globalAgent.options.ca = [...tls.rootCertificates, selfSignedCert];
} catch (error) {
console.error(
'Unable to add the self signed certificate, removing its reference',
error,
);
await asyncStorage.removeItem('server-self-signed-cert');
}
}
const url = await asyncStorage.getItem('server-url'); const url = await asyncStorage.getItem('server-url');
if (!url) { if (!url) {
......
...@@ -88,4 +88,5 @@ export type GlobalPrefs = Partial<{ ...@@ -88,4 +88,5 @@ export type GlobalPrefs = Partial<{
keyId?: string; keyId?: string;
theme: Theme; theme: Theme;
documentDir: string; // Electron only documentDir: string; // Electron only
serverSelfSignedCert: string; // Electron only
}>; }>;
...@@ -41,6 +41,7 @@ module.exports = { ...@@ -41,6 +41,7 @@ module.exports = {
process: require.resolve('process/browser'), process: require.resolve('process/browser'),
stream: require.resolve('stream-browserify'), stream: require.resolve('stream-browserify'),
tls: false, tls: false,
https: false,
// used by memfs in a check which we can ignore I think // used by memfs in a check which we can ignore I think
url: false, url: false,
zlib: require.resolve('browserify-zlib'), zlib: require.resolve('browserify-zlib'),
......
---
category: Maintenance
authors: [MikesGlitch]
---
Support servers with self signed certificates in the Desktop app
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment