125 lines
4.1 KiB
JavaScript
125 lines
4.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
|
|
import {
|
|
applyGatewayPreference,
|
|
buildGatewayPresence,
|
|
createGatewayAutoState,
|
|
nextGatewayAutoState,
|
|
probeGatewayPresence,
|
|
readHostNetworkState,
|
|
verifyGatewayPresence,
|
|
} from '../../src/server/gatewayPresence.js';
|
|
|
|
const subscriptionUrl = 'https://subscription.example/0123456789abcdef0123456789abcdef';
|
|
const nonce = '0123456789abcdef0123456789abcdef';
|
|
|
|
test('Gateway presence is authenticated by the shared subscription secret', async () => {
|
|
const payload = buildGatewayPresence({
|
|
appMode: 'gateway',
|
|
subscriptionUrl,
|
|
gatewayId: 'gateway-1',
|
|
nonce,
|
|
});
|
|
|
|
assert.equal(verifyGatewayPresence(payload, { subscriptionUrl, nonce }), true);
|
|
assert.equal(verifyGatewayPresence(payload, {
|
|
subscriptionUrl: 'https://subscription.example/fedcba9876543210fedcba9876543210',
|
|
nonce,
|
|
}), false);
|
|
|
|
const result = await probeGatewayPresence({
|
|
gateway: '192.168.50.111',
|
|
subscriptionUrl,
|
|
nonce,
|
|
fetchImpl: async (url) => {
|
|
assert.equal(
|
|
url,
|
|
`http://192.168.50.111:3456/api/gateway-presence?nonce=${nonce}`,
|
|
);
|
|
return { ok: true, json: async () => payload };
|
|
},
|
|
});
|
|
assert.deepEqual(result, { gatewayId: 'gateway-1' });
|
|
|
|
assert.equal(buildGatewayPresence({
|
|
appMode: 'gateway',
|
|
subscriptionUrl: 'https://subscription.example/public-feed',
|
|
gatewayId: 'gateway-1',
|
|
nonce,
|
|
}).available, false);
|
|
|
|
const sharedPublicValue = 'https://public.example/sing-box-configuration-v1';
|
|
const firstUrl = `${subscriptionUrl}?redirect=${encodeURIComponent(sharedPublicValue)}`;
|
|
const secondUrl = `https://subscription.example/fedcba9876543210fedcba9876543210?redirect=${encodeURIComponent(sharedPublicValue)}`;
|
|
const firstPayload = buildGatewayPresence({
|
|
appMode: 'gateway',
|
|
subscriptionUrl: firstUrl,
|
|
gatewayId: 'gateway-1',
|
|
nonce,
|
|
});
|
|
assert.equal(verifyGatewayPresence(firstPayload, {
|
|
subscriptionUrl: secondUrl,
|
|
nonce,
|
|
}), false);
|
|
});
|
|
|
|
test('host route freshness and Gateway failures drive a safe automatic fallback', () => {
|
|
const now = Date.now();
|
|
const statePath = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'harbor-route-')), 'network.json');
|
|
fs.writeFileSync(statePath, JSON.stringify({
|
|
gateway: '192.168.50.111',
|
|
interface: 'en0',
|
|
mac: 'aa:bb:cc:dd:ee:ff',
|
|
observedAt: new Date(now).toISOString(),
|
|
}));
|
|
const network = readHostNetworkState(statePath, { now });
|
|
assert.equal(network.gateway, '192.168.50.111');
|
|
|
|
let state = nextGatewayAutoState(createGatewayAutoState(), {
|
|
network,
|
|
verifiedGateway: { gatewayId: 'gateway-1' },
|
|
});
|
|
assert.equal(state.mode, 'gateway-direct');
|
|
|
|
state = nextGatewayAutoState(state, { network });
|
|
state = nextGatewayAutoState(state, { network });
|
|
assert.equal(state.mode, 'gateway-direct');
|
|
state = nextGatewayAutoState(state, { network });
|
|
assert.equal(state.mode, 'local-vpn');
|
|
|
|
const newNetwork = { ...network, mac: '11:22:33:44:55:66' };
|
|
state = nextGatewayAutoState(nextGatewayAutoState(createGatewayAutoState(), {
|
|
network,
|
|
verifiedGateway: { gatewayId: 'gateway-1' },
|
|
}), { network: newNetwork });
|
|
assert.equal(state.mode, 'local-vpn');
|
|
|
|
assert.equal(readHostNetworkState(statePath, { now: now + 16_000 }), null);
|
|
|
|
fs.writeFileSync(statePath, JSON.stringify({
|
|
gateway: '192.168.50.111',
|
|
interface: 'en0',
|
|
mac: '',
|
|
observedAt: new Date(now).toISOString(),
|
|
}));
|
|
assert.equal(readHostNetworkState(statePath, { now }), null);
|
|
});
|
|
|
|
test('client can ignore and restore a verified Gateway without losing discovery', () => {
|
|
const detected = {
|
|
...createGatewayAutoState(),
|
|
mode: 'gateway-direct',
|
|
gatewayId: 'gateway-1',
|
|
};
|
|
|
|
const ignored = applyGatewayPreference(detected, false);
|
|
assert.equal(ignored.mode, 'local-vpn');
|
|
assert.equal(ignored.gatewayId, 'gateway-1');
|
|
assert.equal(applyGatewayPreference(ignored, true).mode, 'gateway-direct');
|
|
assert.equal(applyGatewayPreference(createGatewayAutoState(), true).mode, 'local-vpn');
|
|
});
|