172 lines
6.8 KiB
TypeScript
172 lines
6.8 KiB
TypeScript
|
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||
|
|
import * as smartdb from '../ts/index.js';
|
||
|
|
import { MongoClient } from 'mongodb';
|
||
|
|
import * as fs from 'fs';
|
||
|
|
import * as net from 'net';
|
||
|
|
import * as os from 'os';
|
||
|
|
import * as path from 'path';
|
||
|
|
|
||
|
|
// Static test-only CA and server certificate. The private key is intentionally
|
||
|
|
// non-secret test fixture material and must not be reused outside tests.
|
||
|
|
const CA_PEM = `-----BEGIN CERTIFICATE-----
|
||
|
|
MIIDFTCCAf2gAwIBAgIUXQlk6FLuWELDKLw9KXi0UIYmU50wDQYJKoZIhvcNAQEL
|
||
|
|
BQAwGjEYMBYGA1UEAwwPU21hcnREQiBUZXN0IENBMB4XDTI2MDQyOTIxMjYxNFoX
|
||
|
|
DTM2MDQyNjIxMjYxNFowGjEYMBYGA1UEAwwPU21hcnREQiBUZXN0IENBMIIBIjAN
|
||
|
|
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApnRgZvodreKEKkSodwgDe2JKsA3N
|
||
|
|
GC4c7dmqmOBRQst0OYRoW0kjHnzCVHoGlMTAnjJWXRayPeJCroSA0WhEZIjgHAjW
|
||
|
|
FuWIr+MUYdCG7czdbDEqZYGsrBDUwv+ydgsDNhLKtbfVfcJckdmFp+TT+Po3sf8o
|
||
|
|
u5AfOlcjhM22reBLhZJ2FfM2IbqygRbBxNvU3tH5E1kgu2CpYieXQsmqBwkOPM0S
|
||
|
|
fgkCjlqFeeqV7Jjdq1P6srIItzg6n8/5KGBTxc7VB11WxVAZMIxnOtwpOCpSjbiy
|
||
|
|
jymBLKvyZxklWGpG9HT6RzUTdp0WpwnO7FlbYqD97jrbwA7PfhbJVUkTeQIDAQAB
|
||
|
|
o1MwUTAdBgNVHQ4EFgQUaqFWiFvibBYpJjluNW4XlocmqOQwHwYDVR0jBBgwFoAU
|
||
|
|
aqFWiFvibBYpJjluNW4XlocmqOQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
|
||
|
|
AQsFAAOCAQEAdbmRCxeHwfq6Mw0BRXWYM81xrzDMDBwLkIyaVkBJXCEX4Ybj8QHv
|
||
|
|
tplNqgQae1Hr1qYyNzkivDI/hPnvv/wDsAnT8Wz0/udPpcASTXC03xhRtFXwBSGq
|
||
|
|
2GtLa53cZHJLoGu1S2ntM6Xo3gropXSx/+LIfefsQvqRO/5WxRrEE10OiFr19rA7
|
||
|
|
md0nD6zXdwrMRghu6ACuxX6Ext6QJbTL4r1UGbHg2a9UbdBjcb8sfFPLyEjiLpBK
|
||
|
|
DYvRjddKOwbOpFPoLwmed59Pa6bcqT9NnkRHL+aXUm3M3HfVhNKae7JJShUmCzdx
|
||
|
|
rbKNJQAUp/mMHnBOSxYS7aqgwBKCiKtP4A==
|
||
|
|
-----END CERTIFICATE-----
|
||
|
|
`;
|
||
|
|
|
||
|
|
const SERVER_CERT_PEM = `-----BEGIN CERTIFICATE-----
|
||
|
|
MIIDPTCCAiWgAwIBAgIUMfuX4VHvVJ8Vo6o1U2+f7MHU7dowDQYJKoZIhvcNAQEL
|
||
|
|
BQAwGjEYMBYGA1UEAwwPU21hcnREQiBUZXN0IENBMB4XDTI2MDQyOTIxMjYxNFoX
|
||
|
|
DTM2MDQyNjIxMjYxNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG
|
||
|
|
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5eFz1q4juQsEE7cPN5eFrLvRJW/zOMGBmiet
|
||
|
|
VTQSqVZ/3j3NBWsgxK2xQnNbEXGMlTEE11ih0cCQacc/JnbuvwOt3QX8X6oy4pmb
|
||
|
|
LMGQJEk2FgdpP6OtGqqYbt/fT7QBY39nt6z/RzxYZI7t5g/nkHnlzmzD+ila6k9b
|
||
|
|
TzBSfSmtHHKW/c6az/Dh/xe50zDgrzlBA7e5zoleKqRJFRZlDnDoLyx0EOUbbTbQ
|
||
|
|
vipMynP5bq8l6Fc0N9DAWmXvV4o2x0ZQjfEx5LTvbxNkVWtv8w9w4t4vAZqXwrXd
|
||
|
|
5OZETMWdy7ezxL0E9Snwc6sSfatlVenD/8P5hWJ/C0vCiw21RwIDAQABo4GAMH4w
|
||
|
|
GgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMAsGA1UdDwQEAwIFoDATBgNVHSUE
|
||
|
|
DDAKBggrBgEFBQcDATAdBgNVHQ4EFgQUK2nSXereMZek6gxLweY1AVt9OaswHwYD
|
||
|
|
VR0jBBgwFoAUaqFWiFvibBYpJjluNW4XlocmqOQwDQYJKoZIhvcNAQELBQADggEB
|
||
|
|
AAkC6suxamn+OEmJLMqgaGCvEtFbob5pMijYC32vJNPev+bUHMOB4Oo0FyO59sX3
|
||
|
|
zfLLwk7jagbWJi37T714aSjyJwUHd4XA7McSabP4+1hOOL0NqfiE4yRnxPhlvf3E
|
||
|
|
9otoStAAJ86067DwIs5id7jYm+qrxn6bL+P1h+P1tYxnPOoD0v1cHVbtUNV2tH2E
|
||
|
|
eBhdtTbF+NHrj+oXFGI3jiI7qcwpJ9DFUo/w0sC0POY0T5aWl4ptSXVgEc7nkE91
|
||
|
|
bbPOPyoMjjZ4WhKAW5UzfOafB0bO7+4E0GHcAkBJmS4V8g5qt56nftr+d58R/odY
|
||
|
|
0hQjpoIwzl9RCEW0h8xkqMQ=
|
||
|
|
-----END CERTIFICATE-----
|
||
|
|
`;
|
||
|
|
|
||
|
|
const SERVER_KEY_PEM = `-----BEGIN PRIVATE KEY-----
|
||
|
|
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDl4XPWriO5CwQT
|
||
|
|
tw83l4Wsu9Elb/M4wYGaJ61VNBKpVn/ePc0FayDErbFCc1sRcYyVMQTXWKHRwJBp
|
||
|
|
xz8mdu6/A63dBfxfqjLimZsswZAkSTYWB2k/o60aqphu399PtAFjf2e3rP9HPFhk
|
||
|
|
ju3mD+eQeeXObMP6KVrqT1tPMFJ9Ka0ccpb9zprP8OH/F7nTMOCvOUEDt7nOiV4q
|
||
|
|
pEkVFmUOcOgvLHQQ5RttNtC+KkzKc/luryXoVzQ30MBaZe9XijbHRlCN8THktO9v
|
||
|
|
E2RVa2/zD3Di3i8BmpfCtd3k5kRMxZ3Lt7PEvQT1KfBzqxJ9q2VV6cP/w/mFYn8L
|
||
|
|
S8KLDbVHAgMBAAECggEAAInWJR8US1cow8kOepFayUxJUZ6hAbWGUa+dGtF757Sh
|
||
|
|
qQoZBFW7ZmqHu0Gc6X4MF79dJQn6mwyp6e2DCtqFdaITEqz0ad7yrpAwilrLtSIM
|
||
|
|
w+FxkCoYejMDF2Nj2QJxbGO8gPQhRu/vvxCMoxjPcImwjZq4nMnjAiB8dMOGte9V
|
||
|
|
av/RoWUOFXqeiJHqAXiE372I4BupwYhGrSUQyuVj3SugDRbzvPepTQNRxaBJQPgy
|
||
|
|
4ZtZ8FjJdPFvlyxv6fmLFULHwPNcS6PLWPuwpj7oEQzG4/Q9ojYj4EPdpoOW7qoH
|
||
|
|
h1Y6ag1vk5A/m9DjvMhIDzmUJmq8mlldxqbCBpH0+QKBgQD3Eh7F0ZXdLQe/aG5t
|
||
|
|
ul9hTv68NZa5M0JzJinB6WjXl2s0bUgIvAE9ZmfUYHs8AMvTu4YwJqsrpMuzFOT9
|
||
|
|
Ct5wBSyFbPzVOt9MYE1Gipxx8RfEMSq7Sp0MjarX3h0Va8ry83NWzrN1CvyP8BQq
|
||
|
|
CuXo/IislCDgPg0uXhLD/7GsWQKBgQDuMEptldCKtpW6CdLdYih6xh0j1mdGU4Kb
|
||
|
|
7mTzo3OU3nDnGXGhqvJt/xpksPl7GPRHYQ1dqRzvLKHDtTJqhkedZBnE6A94LkVl
|
||
|
|
uNJnR8v4PkR9nKKg0uK2ug9VcfSiXUpl2yyYiDc123WjHdwH2U6BV3smb/7KwEvv
|
||
|
|
FWaP7PO6nwKBgAE2w5PxPa1ChWE5YCGF4uYVf0bpdH4gdFkgfOAJB4zXn504VDxG
|
||
|
|
wDLPB/+RIcnfryCxMS2XYwvp2V5d4eokXYdrXxagvHVHvsUfTAHmuHIO3zEFlNIq
|
||
|
|
wa7IG2jIHJh4WRzseUqZ5WPT0/3ZDiBOwWZtpzZB3A99/o6Vw73WycaxAoGAHTeR
|
||
|
|
OaYB4bIJ5bskwYEz4/N/SZEYM/k0cTop6fTnzaAHi2GEncchW7rKGwXWZHIoLMVL
|
||
|
|
5WxEH1aDNUV5vLVh/X1058FrfFt4qcSlEoQtEfNZZWscS8vygWWLUfjbgDsfUCU1
|
||
|
|
cDRtSU71PCACiHfweE8pzQo539b8uYQPg6IWN5MCgYA6z/kvGiBB9xFBUAJPsj+w
|
||
|
|
XW/UGbn7svZaCob+N5RA9Rs/0idv/bO2nAauZyHG/nn6HXII6U5pmRyVqWKhI22q
|
||
|
|
K3J0LCP42Zb6/eYzQPbP1jWHCMaL2QJQGsl4NMZixlnNJV0aG/5CButqzSC/cMbG
|
||
|
|
DX0n+YqqWmCgHWU2csnlAA==
|
||
|
|
-----END PRIVATE KEY-----
|
||
|
|
`;
|
||
|
|
|
||
|
|
let server: smartdb.SmartdbServer;
|
||
|
|
let client: MongoClient;
|
||
|
|
let tmpDir: string;
|
||
|
|
let caPath: string;
|
||
|
|
let certPath: string;
|
||
|
|
let keyPath: string;
|
||
|
|
let port: number;
|
||
|
|
|
||
|
|
function makeTmpDir(): string {
|
||
|
|
return fs.mkdtempSync(path.join(os.tmpdir(), 'smartdb-tls-test-'));
|
||
|
|
}
|
||
|
|
|
||
|
|
function cleanTmpDir(dir: string): void {
|
||
|
|
if (fs.existsSync(dir)) {
|
||
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function getFreePort(): Promise<number> {
|
||
|
|
return await new Promise((resolve, reject) => {
|
||
|
|
const probe = net.createServer();
|
||
|
|
probe.once('error', reject);
|
||
|
|
probe.listen(0, '127.0.0.1', () => {
|
||
|
|
const address = probe.address();
|
||
|
|
if (!address || typeof address === 'string') {
|
||
|
|
probe.close(() => reject(new Error('Failed to allocate TCP port')));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
probe.close(() => resolve(address.port));
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
tap.test('tls: should start server with TLS enabled', async () => {
|
||
|
|
tmpDir = makeTmpDir();
|
||
|
|
port = await getFreePort();
|
||
|
|
caPath = path.join(tmpDir, 'ca.pem');
|
||
|
|
certPath = path.join(tmpDir, 'server.pem');
|
||
|
|
keyPath = path.join(tmpDir, 'server.key');
|
||
|
|
|
||
|
|
fs.writeFileSync(caPath, CA_PEM);
|
||
|
|
fs.writeFileSync(certPath, SERVER_CERT_PEM);
|
||
|
|
fs.writeFileSync(keyPath, SERVER_KEY_PEM, { mode: 0o600 });
|
||
|
|
|
||
|
|
server = new smartdb.SmartdbServer({
|
||
|
|
port,
|
||
|
|
tls: {
|
||
|
|
enabled: true,
|
||
|
|
certPath,
|
||
|
|
keyPath,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
await server.start();
|
||
|
|
|
||
|
|
expect(server.running).toBeTrue();
|
||
|
|
expect(server.getConnectionUri()).toEqual(`mongodb://127.0.0.1:${port}/?tls=true`);
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('tls: should connect with official MongoClient and CA validation', async () => {
|
||
|
|
client = new MongoClient(server.getConnectionUri(), {
|
||
|
|
directConnection: true,
|
||
|
|
serverSelectionTimeoutMS: 5000,
|
||
|
|
tlsCAFile: caPath,
|
||
|
|
});
|
||
|
|
await client.connect();
|
||
|
|
|
||
|
|
const ping = await client.db('admin').command({ ping: 1 });
|
||
|
|
expect(ping.ok).toEqual(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('tls: should support CRUD over encrypted transport', async () => {
|
||
|
|
const collection = client.db('tlsdb').collection('notes');
|
||
|
|
const inserted = await collection.insertOne({ title: 'encrypted transport' });
|
||
|
|
expect(inserted.acknowledged).toBeTrue();
|
||
|
|
|
||
|
|
const doc = await collection.findOne({ _id: inserted.insertedId });
|
||
|
|
expect(doc).toBeTruthy();
|
||
|
|
expect(doc!.title).toEqual('encrypted transport');
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('tls: cleanup', async () => {
|
||
|
|
await client.close();
|
||
|
|
await server.stop();
|
||
|
|
expect(server.running).toBeFalse();
|
||
|
|
cleanTmpDir(tmpDir);
|
||
|
|
});
|
||
|
|
|
||
|
|
export default tap.start();
|