/* * Copyright 2024 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import * as assert from 'assert'; import * as path from 'path'; import * as fs from 'fs/promises'; import { experimental } from '../src'; describe('Certificate providers', () => { describe('File watcher', () => { const [caPath, keyPath, certPath] = ['ca.pem', 'server1.key', 'server1.pem'].map(file => path.join(__dirname, 'fixtures', file)); let caData: Buffer, keyData: Buffer, certData: Buffer; before(async () => { [caData, keyData, certData] = await Promise.all([caPath, keyPath, certPath].map(filePath => fs.readFile(filePath))); }); it('Should reject a config with no files', () => { const config: experimental.FileWatcherCertificateProviderConfig = { refreshIntervalMs: 1000 }; assert.throws(() => { new experimental.FileWatcherCertificateProvider(config); }); }); it('Should accept a config with just a CA certificate', () => { const config: experimental.FileWatcherCertificateProviderConfig = { caCertificateFile: caPath, refreshIntervalMs: 1000 }; assert.doesNotThrow(() => { new experimental.FileWatcherCertificateProvider(config); }); }); it('Should accept a config with just a key and certificate', () => { const config: experimental.FileWatcherCertificateProviderConfig = { certificateFile: certPath, privateKeyFile: keyPath, refreshIntervalMs: 1000 }; assert.doesNotThrow(() => { new experimental.FileWatcherCertificateProvider(config); }); }); it('Should accept a config with all files', () => { const config: experimental.FileWatcherCertificateProviderConfig = { caCertificateFile: caPath, certificateFile: certPath, privateKeyFile: keyPath, refreshIntervalMs: 1000 }; assert.doesNotThrow(() => { new experimental.FileWatcherCertificateProvider(config); }); }); it('Should reject a config with a key but no certificate', () => { const config: experimental.FileWatcherCertificateProviderConfig = { caCertificateFile: caPath, privateKeyFile: keyPath, refreshIntervalMs: 1000 }; assert.throws(() => { new experimental.FileWatcherCertificateProvider(config); }); }); it('Should reject a config with a certificate but no key', () => { const config: experimental.FileWatcherCertificateProviderConfig = { caCertificateFile: caPath, privateKeyFile: keyPath, refreshIntervalMs: 1000 }; assert.throws(() => { new experimental.FileWatcherCertificateProvider(config); }); }); it('Should find the CA file when configured for it', done => { const config: experimental.FileWatcherCertificateProviderConfig = { caCertificateFile: caPath, refreshIntervalMs: 1000 }; const provider = new experimental.FileWatcherCertificateProvider(config); const listener: experimental.CaCertificateUpdateListener = update => { if (update) { provider.removeCaCertificateListener(listener); assert(update.caCertificate.equals(caData)); done(); } }; provider.addCaCertificateListener(listener); }); it('Should find the identity certificate files when configured for it', done => { const config: experimental.FileWatcherCertificateProviderConfig = { certificateFile: certPath, privateKeyFile: keyPath, refreshIntervalMs: 1000 }; const provider = new experimental.FileWatcherCertificateProvider(config); const listener: experimental.IdentityCertificateUpdateListener = update => { if (update) { provider.removeIdentityCertificateListener(listener); assert(update.certificate.equals(certData)); assert(update.privateKey.equals(keyData)); done(); } }; provider.addIdentityCertificateListener(listener); }); it('Should find all files when configured for it', done => { const config: experimental.FileWatcherCertificateProviderConfig = { caCertificateFile: caPath, certificateFile: certPath, privateKeyFile: keyPath, refreshIntervalMs: 1000 }; const provider = new experimental.FileWatcherCertificateProvider(config); let seenCaUpdate = false; let seenIdentityUpdate = false; const caListener: experimental.CaCertificateUpdateListener = update => { if (update) { provider.removeCaCertificateListener(caListener); assert(update.caCertificate.equals(caData)); seenCaUpdate = true; if (seenIdentityUpdate) { done(); } } }; const identityListener: experimental.IdentityCertificateUpdateListener = update => { if (update) { provider.removeIdentityCertificateListener(identityListener); assert(update.certificate.equals(certData)); assert(update.privateKey.equals(keyData)); seenIdentityUpdate = true; if (seenCaUpdate) { done(); } } }; provider.addCaCertificateListener(caListener); provider.addIdentityCertificateListener(identityListener); }); }); });