96 lines
3.1 KiB
TypeScript
96 lines
3.1 KiB
TypeScript
// Copyright (c) 2015-2016 Yuya Ochiai
|
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
'use strict';
|
|
|
|
import fs from 'fs';
|
|
|
|
import {Certificate, ipcMain} from 'electron';
|
|
|
|
import {ComparableCertificate} from 'types/certificate';
|
|
|
|
import {UPDATE_PATHS} from 'common/communication';
|
|
import {Logger} from 'common/log';
|
|
import * as Validator from 'common/Validator';
|
|
|
|
import {certificateStorePath} from './constants';
|
|
|
|
function comparableCertificate(certificate: Certificate, dontTrust = false): ComparableCertificate {
|
|
return {
|
|
data: certificate.data.toString(),
|
|
issuerName: certificate.issuerName,
|
|
dontTrust,
|
|
};
|
|
}
|
|
|
|
function areEqual(certificate0: ComparableCertificate, certificate1: ComparableCertificate) {
|
|
if (certificate0.data !== certificate1.data) {
|
|
return false;
|
|
}
|
|
if (certificate0.issuerName !== certificate1.issuerName) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export class CertificateStore {
|
|
storeFile: string;
|
|
data: Record<string, ComparableCertificate>;
|
|
|
|
constructor(storeFile: string) {
|
|
this.storeFile = storeFile;
|
|
let storeStr;
|
|
try {
|
|
storeStr = fs.readFileSync(storeFile, 'utf-8');
|
|
const result = Validator.validateCertificateStore(storeStr);
|
|
if (!result) {
|
|
throw new Error('Provided certificate store file does not validate, using defaults instead.');
|
|
}
|
|
this.data = result;
|
|
} catch (e) {
|
|
this.data = {};
|
|
}
|
|
}
|
|
|
|
save = () => {
|
|
fs.writeFileSync(this.storeFile, JSON.stringify(this.data, null, ' '));
|
|
};
|
|
|
|
add = (targetURL: URL, certificate: Certificate, dontTrust = false) => {
|
|
const comparableCert = comparableCertificate(certificate, dontTrust);
|
|
this.data[targetURL.origin] = comparableCert;
|
|
|
|
// Trust certificate for websocket connections on the same origin.
|
|
if (targetURL.origin.startsWith('https://')) {
|
|
const wssHost = targetURL.origin.replace('https', 'wss');
|
|
this.data[wssHost] = comparableCert;
|
|
}
|
|
};
|
|
|
|
isExisting = (targetURL: URL) => {
|
|
return Object.prototype.hasOwnProperty.call(this.data, targetURL.origin);
|
|
};
|
|
|
|
isTrusted = (targetURL: URL, certificate: Certificate) => {
|
|
if (!this.isExisting(targetURL)) {
|
|
return false;
|
|
}
|
|
return areEqual(this.data[targetURL.origin], comparableCertificate(certificate));
|
|
};
|
|
|
|
isExplicitlyUntrusted = (targetURL: URL) => {
|
|
// Whether or not the certificate was explicitly marked as untrusted by
|
|
// clicking "Don't ask again" checkbox before cancelling the connection.
|
|
const dontTrust = this.data[targetURL.origin]?.dontTrust;
|
|
return dontTrust === undefined ? false : dontTrust;
|
|
}
|
|
}
|
|
|
|
let certificateStore = new CertificateStore(certificateStorePath);
|
|
export default certificateStore;
|
|
|
|
ipcMain.on(UPDATE_PATHS, () => {
|
|
new Logger('certificateStore').debug('UPDATE_PATHS');
|
|
certificateStore = new CertificateStore(certificateStorePath);
|
|
});
|