|
"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 = undefined; |
|
this.caListeners = new Set(); |
|
this.latestIdentityUpdate = undefined; |
|
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.status + ', privateKey ' + privateKeyResult.status + ', CA certificate ' + caCertificateResult.status); |
|
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 |
|
}; |
|
} |
|
else { |
|
this.latestCaUpdate = null; |
|
} |
|
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 = undefined; |
|
this.latestIdentityUpdate = undefined; |
|
} |
|
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(); |
|
if (this.latestCaUpdate !== undefined) { |
|
process.nextTick(listener, this.latestCaUpdate); |
|
} |
|
} |
|
removeCaCertificateListener(listener) { |
|
this.caListeners.delete(listener); |
|
this.maybeStopWatchingFiles(); |
|
} |
|
addIdentityCertificateListener(listener) { |
|
this.identityListeners.add(listener); |
|
this.maybeStartWatchingFiles(); |
|
if (this.latestIdentityUpdate !== undefined) { |
|
process.nextTick(listener, this.latestIdentityUpdate); |
|
} |
|
} |
|
removeIdentityCertificateListener(listener) { |
|
this.identityListeners.delete(listener); |
|
this.maybeStopWatchingFiles(); |
|
} |
|
} |
|
exports.FileWatcherCertificateProvider = FileWatcherCertificateProvider; |
|
|