Integrate upstream-compatible read-only MeshCentral transport
This commit is contained in:
@@ -19,9 +19,7 @@ import {
|
||||
validateCredentials,
|
||||
} from './contracts';
|
||||
import { createMockAdapter } from './mock-adapter';
|
||||
import {
|
||||
MeshCtrlClient,
|
||||
} from './meshctrl-client';
|
||||
import { UpstreamMeshTransport } from './upstream-transport';
|
||||
import {
|
||||
createAuditEntry,
|
||||
AuditAction,
|
||||
@@ -40,7 +38,7 @@ export class MeshCentralRealAdapter implements MeshCentralAdapter {
|
||||
readonly version = '0.1.0';
|
||||
|
||||
private config: BridgeConfig;
|
||||
private client: MeshCtrlClient | null = null;
|
||||
private client: UpstreamMeshTransport | null = null;
|
||||
private credentials: { username: string; password: string; totpToken?: string } | null = null;
|
||||
private connected = false;
|
||||
private logger: Logger;
|
||||
@@ -188,12 +186,9 @@ export class MeshCentralRealAdapter implements MeshCentralAdapter {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.config.adapterType === 'real' && (!hasUsername || !hasPassword)) {
|
||||
throw new BridgeError(
|
||||
ErrorCodes.ConfigurationInvalid,
|
||||
'Real adapter requires authentication credentials',
|
||||
);
|
||||
}
|
||||
// Real-adapter credentials may be supplied later through setCredentials().
|
||||
// Keep the constructor usable for protected runtime secret injection;
|
||||
// _ensureConnected() remains the enforcement point before any connection.
|
||||
|
||||
// Validate credential formats if present
|
||||
if (hasUsername && hasPassword) {
|
||||
@@ -297,10 +292,7 @@ export class MeshCentralRealAdapter implements MeshCentralAdapter {
|
||||
);
|
||||
}
|
||||
|
||||
this.client = new MeshCtrlClient({
|
||||
url: this.config.meshCentralUrl,
|
||||
timeoutMs: this.config.timeoutMs,
|
||||
});
|
||||
this.client = new UpstreamMeshTransport(this.config.meshCentralUrl);
|
||||
|
||||
try {
|
||||
await this.client.connect(this.credentials);
|
||||
@@ -402,34 +394,6 @@ export class MeshCentralRealAdapter implements MeshCentralAdapter {
|
||||
groupId: request?.groupId,
|
||||
});
|
||||
|
||||
// Get server version first to verify connection
|
||||
try {
|
||||
const versionResponse = await this.client!.sendRequest({
|
||||
action: 'serverversion',
|
||||
});
|
||||
|
||||
if (versionResponse.err) {
|
||||
this._auditLog(AuditAction.MESHCENTRAL_REQUEST, AuditStatus.FAILURE, {
|
||||
actor: this._getActorName(),
|
||||
resource: 'serverversion',
|
||||
error: versionResponse.err,
|
||||
});
|
||||
|
||||
throw new BridgeError(
|
||||
ErrorCodes.MeshCentralError,
|
||||
'Failed to establish connection with MeshCentral server',
|
||||
);
|
||||
} else {
|
||||
this._auditLog(AuditAction.MESHCENTRAL_RESPONSE, AuditStatus.SUCCESS, {
|
||||
actor: this._getActorName(),
|
||||
resource: 'serverversion',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
await this._disconnect();
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Get meshes (device groups)
|
||||
const meshesResponse = await this.client!.sendRequest({
|
||||
action: 'meshes',
|
||||
@@ -442,7 +406,10 @@ export class MeshCentralRealAdapter implements MeshCentralAdapter {
|
||||
);
|
||||
}
|
||||
|
||||
const meshes = (meshesResponse.meshes ?? []) as Array<{ id: string; name: string }>;
|
||||
const meshes = ((meshesResponse.meshes ?? []) as Array<{ id?: string; _id?: string; name: string; description?: string; desc?: string }>).map((mesh) => ({
|
||||
...mesh,
|
||||
id: mesh.id ?? mesh._id ?? '',
|
||||
})).filter((mesh) => mesh.id !== '') as Array<{ id: string; name: string }>;
|
||||
|
||||
// If group filter provided, find matching mesh
|
||||
if (request?.groupId) {
|
||||
@@ -489,7 +456,29 @@ export class MeshCentralRealAdapter implements MeshCentralAdapter {
|
||||
);
|
||||
}
|
||||
|
||||
const nodes = (nodesResponse.nodes ?? []) as Array<{ id: string; name: string; online: boolean; meshid?: string; device?: { os?: string; uptime?: number; lastseen?: number; ip?: string[]; agentver?: string } }>;
|
||||
const groupedNodes = (nodesResponse.nodes ?? {}) as Record<string, Array<{
|
||||
id?: string;
|
||||
_id?: string;
|
||||
name: string;
|
||||
meshid?: string;
|
||||
online?: boolean;
|
||||
conn?: number;
|
||||
osdesc?: string;
|
||||
ip?: string[];
|
||||
device?: { os?: string; uptime?: number; lastseen?: number; ip?: string[]; agentver?: string };
|
||||
}>>;
|
||||
const nodes = Object.entries(groupedNodes).flatMap(([groupId, groupNodes]) =>
|
||||
groupNodes.map((node) => ({
|
||||
id: node.id ?? node._id ?? '',
|
||||
name: node.name,
|
||||
meshid: node.meshid ?? groupId,
|
||||
online: node.online ?? node.conn === 1,
|
||||
device: node.device ?? {
|
||||
os: node.osdesc,
|
||||
ip: node.ip,
|
||||
},
|
||||
})),
|
||||
).filter((node) => node.id !== '') as Array<{ id: string; name: string; online: boolean; meshid?: string; device?: { os?: string; uptime?: number; lastseen?: number; ip?: string[]; agentver?: string } }>;
|
||||
|
||||
if (!this._isGroupAllowed(mesh.id)) {
|
||||
return [];
|
||||
@@ -563,7 +552,17 @@ export class MeshCentralRealAdapter implements MeshCentralAdapter {
|
||||
);
|
||||
}
|
||||
|
||||
const meshes = (meshesResponse.meshes ?? []) as Array<{ id: string; name: string; description?: string }>;
|
||||
const meshes = ((meshesResponse.meshes ?? []) as Array<{
|
||||
id?: string;
|
||||
_id?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
desc?: string;
|
||||
}>).map((mesh) => ({
|
||||
...mesh,
|
||||
id: mesh.id ?? mesh._id ?? '',
|
||||
description: mesh.description ?? mesh.desc,
|
||||
})).filter((mesh) => mesh.id !== '') as Array<{ id: string; name: string; description?: string }>;
|
||||
|
||||
// Filter by group allowlist if configured
|
||||
const allowedMeshes = this._filterByAllowlist(
|
||||
@@ -660,11 +659,11 @@ export class MeshCentralRealAdapter implements MeshCentralAdapter {
|
||||
try {
|
||||
await this._ensureConnected();
|
||||
|
||||
const versionResponse = await this.client!.sendRequest({
|
||||
action: 'serverversion',
|
||||
});
|
||||
|
||||
const healthy = !versionResponse.err;
|
||||
// `meshes` is the least-privileged authenticated inventory operation
|
||||
// and is available without the server-upgrade site permission required
|
||||
// by MeshCentral's `serverversion` command.
|
||||
const meshesResponse = await this.client!.sendRequest({ action: 'meshes' });
|
||||
const healthy = Array.isArray(meshesResponse.meshes);
|
||||
this._auditLog(AuditAction.SERVICE_START, healthy ? AuditStatus.SUCCESS : AuditStatus.FAILURE, {
|
||||
actor: this._getActorName(),
|
||||
resource: 'health-check',
|
||||
|
||||
Reference in New Issue
Block a user