| "use strict"; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.FileWatcherCertificateProvider = void 0; |
| const fs = require("fs"); |
| const logging = require("./logging"); |
| const constants_1 = require("./constants"); |
| const util_1 = require("util"); |
| const TRACER_NAME = 'certificate_provider'; |
| function trace(text) { |
| logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, text); |
| } |
| const readFilePromise = (0, util_1.promisify)(fs.readFile); |
| class FileWatcherCertificateProvider { |
| constructor(config) { |
| this.config = config; |
| this.refreshTimer = null; |
| this.fileResultPromise = null; |
| this.latestCaUpdate = null; |
| this.caListeners = new Set(); |
| this.latestIdentityUpdate = null; |
| this.identityListeners = new Set(); |
| this.lastUpdateTime = null; |
| if ((config.certificateFile === undefined) !== (config.privateKeyFile === undefined)) { |
| throw new Error('certificateFile and privateKeyFile must be set or unset together'); |
| } |
| if (config.certificateFile === undefined && config.caCertificateFile === undefined) { |
| throw new Error('At least one of certificateFile and caCertificateFile must be set'); |
| } |
| trace('File watcher constructed with config ' + JSON.stringify(config)); |
| } |
| updateCertificates() { |
| if (this.fileResultPromise) { |
| return; |
| } |
| this.fileResultPromise = Promise.allSettled([ |
| this.config.certificateFile ? readFilePromise(this.config.certificateFile) : Promise.reject(), |
| this.config.privateKeyFile ? readFilePromise(this.config.privateKeyFile) : Promise.reject(), |
| this.config.caCertificateFile ? readFilePromise(this.config.caCertificateFile) : Promise.reject() |
| ]); |
| this.fileResultPromise.then(([certificateResult, privateKeyResult, caCertificateResult]) => { |
| if (!this.refreshTimer) { |
| return; |
| } |
| trace('File watcher read certificates certificate' + (certificateResult ? '!=' : '==') + 'null, privateKey' + (privateKeyResult ? '!=' : '==') + 'null, CA certificate' + (caCertificateResult ? '!=' : '==') + 'null'); |
| this.lastUpdateTime = new Date(); |
| this.fileResultPromise = null; |
| if (certificateResult.status === 'fulfilled' && privateKeyResult.status === 'fulfilled') { |
| this.latestIdentityUpdate = { |
| certificate: certificateResult.value, |
| privateKey: privateKeyResult.value |
| }; |
| } |
| else { |
| this.latestIdentityUpdate = null; |
| } |
| if (caCertificateResult.status === 'fulfilled') { |
| this.latestCaUpdate = { |
| caCertificate: caCertificateResult.value |
| }; |
| } |
| for (const listener of this.identityListeners) { |
| listener(this.latestIdentityUpdate); |
| } |
| for (const listener of this.caListeners) { |
| listener(this.latestCaUpdate); |
| } |
| }); |
| trace('File watcher initiated certificate update'); |
| } |
| maybeStartWatchingFiles() { |
| if (!this.refreshTimer) { |
| |
| |
| |
| |
| const timeSinceLastUpdate = this.lastUpdateTime ? (new Date()).getTime() - this.lastUpdateTime.getTime() : Infinity; |
| if (timeSinceLastUpdate > this.config.refreshIntervalMs) { |
| this.updateCertificates(); |
| } |
| if (timeSinceLastUpdate > this.config.refreshIntervalMs * 2) { |
| |
| this.latestCaUpdate = null; |
| this.latestIdentityUpdate = null; |
| } |
| this.refreshTimer = setInterval(() => this.updateCertificates(), this.config.refreshIntervalMs); |
| trace('File watcher started watching'); |
| } |
| } |
| maybeStopWatchingFiles() { |
| if (this.caListeners.size === 0 && this.identityListeners.size === 0) { |
| this.fileResultPromise = null; |
| if (this.refreshTimer) { |
| clearInterval(this.refreshTimer); |
| this.refreshTimer = null; |
| } |
| } |
| } |
| addCaCertificateListener(listener) { |
| this.caListeners.add(listener); |
| this.maybeStartWatchingFiles(); |
| process.nextTick(listener, this.latestCaUpdate); |
| } |
| removeCaCertificateListener(listener) { |
| this.caListeners.delete(listener); |
| this.maybeStopWatchingFiles(); |
| } |
| addIdentityCertificateListener(listener) { |
| this.identityListeners.add(listener); |
| this.maybeStartWatchingFiles(); |
| process.nextTick(listener, this.latestIdentityUpdate); |
| } |
| removeIdentityCertificateListener(listener) { |
| this.identityListeners.delete(listener); |
| this.maybeStopWatchingFiles(); |
| } |
| } |
| exports.FileWatcherCertificateProvider = FileWatcherCertificateProvider; |
| |