@serve.zone/dcrouter 12.1.0 → 12.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_serve/bundle.js +750 -688
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +6 -1
- package/dist_ts/classes.dcrouter.js +11 -3
- package/dist_ts/config/classes.db-seeder.d.ts +25 -0
- package/dist_ts/config/classes.db-seeder.js +69 -0
- package/dist_ts/config/classes.reference-resolver.d.ts +80 -0
- package/dist_ts/config/classes.reference-resolver.js +482 -0
- package/dist_ts/config/classes.route-config-manager.d.ts +13 -3
- package/dist_ts/config/classes.route-config-manager.js +53 -3
- package/dist_ts/config/index.d.ts +2 -0
- package/dist_ts/config/index.js +3 -1
- package/dist_ts/db/documents/classes.network-target.doc.d.ts +15 -0
- package/dist_ts/db/documents/classes.network-target.doc.js +118 -0
- package/dist_ts/db/documents/classes.security-profile.doc.d.ts +16 -0
- package/dist_ts/db/documents/classes.security-profile.doc.js +118 -0
- package/dist_ts/db/documents/classes.stored-route.doc.d.ts +2 -0
- package/dist_ts/db/documents/classes.stored-route.doc.js +8 -2
- package/dist_ts/db/documents/index.d.ts +2 -0
- package/dist_ts/db/documents/index.js +3 -1
- package/dist_ts/opsserver/classes.opsserver.d.ts +2 -0
- package/dist_ts/opsserver/classes.opsserver.js +5 -1
- package/dist_ts/opsserver/handlers/index.d.ts +2 -0
- package/dist_ts/opsserver/handlers/index.js +3 -1
- package/dist_ts/opsserver/handlers/network-target.handler.d.ts +10 -0
- package/dist_ts/opsserver/handlers/network-target.handler.js +117 -0
- package/dist_ts/opsserver/handlers/route-management.handler.js +3 -2
- package/dist_ts/opsserver/handlers/security-profile.handler.d.ts +10 -0
- package/dist_ts/opsserver/handlers/security-profile.handler.js +119 -0
- package/dist_ts_interfaces/data/route-management.d.ts +48 -1
- package/dist_ts_interfaces/requests/index.d.ts +2 -0
- package/dist_ts_interfaces/requests/index.js +3 -1
- package/dist_ts_interfaces/requests/network-targets.d.ts +102 -0
- package/dist_ts_interfaces/requests/network-targets.js +2 -0
- package/dist_ts_interfaces/requests/route-management.d.ts +3 -1
- package/dist_ts_interfaces/requests/security-profiles.d.ts +102 -0
- package/dist_ts_interfaces/requests/security-profiles.js +2 -0
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +43 -0
- package/dist_ts_web/appstate.js +176 -2
- package/dist_ts_web/elements/index.d.ts +2 -0
- package/dist_ts_web/elements/index.js +3 -1
- package/dist_ts_web/elements/ops-dashboard.js +13 -1
- package/dist_ts_web/elements/ops-view-networktargets.d.ts +17 -0
- package/dist_ts_web/elements/ops-view-networktargets.js +246 -0
- package/dist_ts_web/elements/ops-view-securityprofiles.d.ts +17 -0
- package/dist_ts_web/elements/ops-view-securityprofiles.js +275 -0
- package/dist_ts_web/router.d.ts +1 -1
- package/dist_ts_web/router.js +2 -2
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +19 -1
- package/ts/config/classes.db-seeder.ts +95 -0
- package/ts/config/classes.reference-resolver.ts +576 -0
- package/ts/config/classes.route-config-manager.ts +64 -1
- package/ts/config/index.ts +3 -1
- package/ts/db/documents/classes.network-target.doc.ts +48 -0
- package/ts/db/documents/classes.security-profile.doc.ts +49 -0
- package/ts/db/documents/classes.stored-route.doc.ts +4 -0
- package/ts/db/documents/index.ts +2 -0
- package/ts/opsserver/classes.opsserver.ts +4 -0
- package/ts/opsserver/handlers/index.ts +3 -1
- package/ts/opsserver/handlers/network-target.handler.ts +167 -0
- package/ts/opsserver/handlers/route-management.handler.ts +2 -1
- package/ts/opsserver/handlers/security-profile.handler.ts +169 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +243 -1
- package/ts_web/elements/index.ts +2 -0
- package/ts_web/elements/ops-dashboard.ts +12 -0
- package/ts_web/elements/ops-view-networktargets.ts +214 -0
- package/ts_web/elements/ops-view-securityprofiles.ts +242 -0
- package/ts_web/router.ts +1 -1
|
@@ -6,9 +6,11 @@ import type {
|
|
|
6
6
|
IRouteOverride,
|
|
7
7
|
IMergedRoute,
|
|
8
8
|
IRouteWarning,
|
|
9
|
+
IRouteMetadata,
|
|
9
10
|
} from '../../ts_interfaces/data/route-management.js';
|
|
10
11
|
import type { IDcRouterRouteConfig } from '../../ts_interfaces/data/remoteingress.js';
|
|
11
12
|
import { type IHttp3Config, augmentRouteWithHttp3 } from '../http3/index.js';
|
|
13
|
+
import type { ReferenceResolver } from './classes.reference-resolver.js';
|
|
12
14
|
|
|
13
15
|
export class RouteConfigManager {
|
|
14
16
|
private storedRoutes = new Map<string, IStoredRoute>();
|
|
@@ -20,8 +22,14 @@ export class RouteConfigManager {
|
|
|
20
22
|
private getSmartProxy: () => plugins.smartproxy.SmartProxy | undefined,
|
|
21
23
|
private getHttp3Config?: () => IHttp3Config | undefined,
|
|
22
24
|
private getVpnAllowList?: (tags?: string[]) => string[],
|
|
25
|
+
private referenceResolver?: ReferenceResolver,
|
|
23
26
|
) {}
|
|
24
27
|
|
|
28
|
+
/** Expose stored routes map for reference resolution lookups. */
|
|
29
|
+
public getStoredRoutes(): Map<string, IStoredRoute> {
|
|
30
|
+
return this.storedRoutes;
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
/**
|
|
26
34
|
* Load persisted routes and overrides, compute warnings, apply to SmartProxy.
|
|
27
35
|
*/
|
|
@@ -62,6 +70,7 @@ export class RouteConfigManager {
|
|
|
62
70
|
storedRouteId: stored.id,
|
|
63
71
|
createdAt: stored.createdAt,
|
|
64
72
|
updatedAt: stored.updatedAt,
|
|
73
|
+
metadata: stored.metadata,
|
|
65
74
|
});
|
|
66
75
|
}
|
|
67
76
|
|
|
@@ -76,6 +85,7 @@ export class RouteConfigManager {
|
|
|
76
85
|
route: plugins.smartproxy.IRouteConfig,
|
|
77
86
|
createdBy: string,
|
|
78
87
|
enabled = true,
|
|
88
|
+
metadata?: IRouteMetadata,
|
|
79
89
|
): Promise<string> {
|
|
80
90
|
const id = plugins.uuid.v4();
|
|
81
91
|
const now = Date.now();
|
|
@@ -85,6 +95,14 @@ export class RouteConfigManager {
|
|
|
85
95
|
route.name = `programmatic-${id.slice(0, 8)}`;
|
|
86
96
|
}
|
|
87
97
|
|
|
98
|
+
// Resolve references if metadata has refs and resolver is available
|
|
99
|
+
let resolvedMetadata = metadata;
|
|
100
|
+
if (metadata && this.referenceResolver) {
|
|
101
|
+
const resolved = this.referenceResolver.resolveRoute(route, metadata);
|
|
102
|
+
route = resolved.route;
|
|
103
|
+
resolvedMetadata = resolved.metadata;
|
|
104
|
+
}
|
|
105
|
+
|
|
88
106
|
const stored: IStoredRoute = {
|
|
89
107
|
id,
|
|
90
108
|
route,
|
|
@@ -92,6 +110,7 @@ export class RouteConfigManager {
|
|
|
92
110
|
createdAt: now,
|
|
93
111
|
updatedAt: now,
|
|
94
112
|
createdBy,
|
|
113
|
+
metadata: resolvedMetadata,
|
|
95
114
|
};
|
|
96
115
|
|
|
97
116
|
this.storedRoutes.set(id, stored);
|
|
@@ -102,7 +121,11 @@ export class RouteConfigManager {
|
|
|
102
121
|
|
|
103
122
|
public async updateRoute(
|
|
104
123
|
id: string,
|
|
105
|
-
patch: {
|
|
124
|
+
patch: {
|
|
125
|
+
route?: Partial<plugins.smartproxy.IRouteConfig>;
|
|
126
|
+
enabled?: boolean;
|
|
127
|
+
metadata?: Partial<IRouteMetadata>;
|
|
128
|
+
},
|
|
106
129
|
): Promise<boolean> {
|
|
107
130
|
const stored = this.storedRoutes.get(id);
|
|
108
131
|
if (!stored) return false;
|
|
@@ -113,6 +136,17 @@ export class RouteConfigManager {
|
|
|
113
136
|
if (patch.enabled !== undefined) {
|
|
114
137
|
stored.enabled = patch.enabled;
|
|
115
138
|
}
|
|
139
|
+
if (patch.metadata !== undefined) {
|
|
140
|
+
stored.metadata = { ...stored.metadata, ...patch.metadata };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Re-resolve if metadata refs exist and resolver is available
|
|
144
|
+
if (stored.metadata && this.referenceResolver) {
|
|
145
|
+
const resolved = this.referenceResolver.resolveRoute(stored.route, stored.metadata);
|
|
146
|
+
stored.route = resolved.route;
|
|
147
|
+
stored.metadata = resolved.metadata;
|
|
148
|
+
}
|
|
149
|
+
|
|
116
150
|
stored.updatedAt = Date.now();
|
|
117
151
|
|
|
118
152
|
await this.persistRoute(stored);
|
|
@@ -188,6 +222,7 @@ export class RouteConfigManager {
|
|
|
188
222
|
createdAt: doc.createdAt,
|
|
189
223
|
updatedAt: doc.updatedAt,
|
|
190
224
|
createdBy: doc.createdBy,
|
|
225
|
+
metadata: doc.metadata,
|
|
191
226
|
});
|
|
192
227
|
}
|
|
193
228
|
}
|
|
@@ -220,6 +255,7 @@ export class RouteConfigManager {
|
|
|
220
255
|
existingDoc.enabled = stored.enabled;
|
|
221
256
|
existingDoc.updatedAt = stored.updatedAt;
|
|
222
257
|
existingDoc.createdBy = stored.createdBy;
|
|
258
|
+
existingDoc.metadata = stored.metadata;
|
|
223
259
|
await existingDoc.save();
|
|
224
260
|
} else {
|
|
225
261
|
const doc = new StoredRouteDoc();
|
|
@@ -229,6 +265,7 @@ export class RouteConfigManager {
|
|
|
229
265
|
doc.createdAt = stored.createdAt;
|
|
230
266
|
doc.updatedAt = stored.updatedAt;
|
|
231
267
|
doc.createdBy = stored.createdBy;
|
|
268
|
+
doc.metadata = stored.metadata;
|
|
232
269
|
await doc.save();
|
|
233
270
|
}
|
|
234
271
|
}
|
|
@@ -277,6 +314,32 @@ export class RouteConfigManager {
|
|
|
277
314
|
}
|
|
278
315
|
}
|
|
279
316
|
|
|
317
|
+
// =========================================================================
|
|
318
|
+
// Re-resolve routes after profile/target changes
|
|
319
|
+
// =========================================================================
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Re-resolve specific routes by ID (after a profile or target is updated).
|
|
323
|
+
* Persists each route and calls applyRoutes() once at the end.
|
|
324
|
+
*/
|
|
325
|
+
public async reResolveRoutes(routeIds: string[]): Promise<void> {
|
|
326
|
+
if (!this.referenceResolver || routeIds.length === 0) return;
|
|
327
|
+
|
|
328
|
+
for (const routeId of routeIds) {
|
|
329
|
+
const stored = this.storedRoutes.get(routeId);
|
|
330
|
+
if (!stored?.metadata) continue;
|
|
331
|
+
|
|
332
|
+
const resolved = this.referenceResolver.resolveRoute(stored.route, stored.metadata);
|
|
333
|
+
stored.route = resolved.route;
|
|
334
|
+
stored.metadata = resolved.metadata;
|
|
335
|
+
stored.updatedAt = Date.now();
|
|
336
|
+
await this.persistRoute(stored);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
await this.applyRoutes();
|
|
340
|
+
logger.log('info', `Re-resolved ${routeIds.length} route(s) after profile/target change`);
|
|
341
|
+
}
|
|
342
|
+
|
|
280
343
|
// =========================================================================
|
|
281
344
|
// Private: apply merged routes to SmartProxy
|
|
282
345
|
// =========================================================================
|
package/ts/config/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// Export validation tools only
|
|
2
2
|
export * from './validator.js';
|
|
3
3
|
export { RouteConfigManager } from './classes.route-config-manager.js';
|
|
4
|
-
export { ApiTokenManager } from './classes.api-token-manager.js';
|
|
4
|
+
export { ApiTokenManager } from './classes.api-token-manager.js';
|
|
5
|
+
export { ReferenceResolver } from './classes.reference-resolver.js';
|
|
6
|
+
export { DbSeeder } from './classes.db-seeder.js';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { DcRouterDb } from '../classes.dcrouter-db.js';
|
|
3
|
+
|
|
4
|
+
const getDb = () => DcRouterDb.getInstance().getDb();
|
|
5
|
+
|
|
6
|
+
@plugins.smartdata.Collection(() => getDb())
|
|
7
|
+
export class NetworkTargetDoc extends plugins.smartdata.SmartDataDbDoc<NetworkTargetDoc, NetworkTargetDoc> {
|
|
8
|
+
@plugins.smartdata.unI()
|
|
9
|
+
@plugins.smartdata.svDb()
|
|
10
|
+
public id!: string;
|
|
11
|
+
|
|
12
|
+
@plugins.smartdata.svDb()
|
|
13
|
+
public name: string = '';
|
|
14
|
+
|
|
15
|
+
@plugins.smartdata.svDb()
|
|
16
|
+
public description?: string;
|
|
17
|
+
|
|
18
|
+
@plugins.smartdata.svDb()
|
|
19
|
+
public host!: string | string[];
|
|
20
|
+
|
|
21
|
+
@plugins.smartdata.svDb()
|
|
22
|
+
public port!: number;
|
|
23
|
+
|
|
24
|
+
@plugins.smartdata.svDb()
|
|
25
|
+
public createdAt!: number;
|
|
26
|
+
|
|
27
|
+
@plugins.smartdata.svDb()
|
|
28
|
+
public updatedAt!: number;
|
|
29
|
+
|
|
30
|
+
@plugins.smartdata.svDb()
|
|
31
|
+
public createdBy!: string;
|
|
32
|
+
|
|
33
|
+
constructor() {
|
|
34
|
+
super();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public static async findById(id: string): Promise<NetworkTargetDoc | null> {
|
|
38
|
+
return await NetworkTargetDoc.getInstance({ id });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public static async findByName(name: string): Promise<NetworkTargetDoc | null> {
|
|
42
|
+
return await NetworkTargetDoc.getInstance({ name });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public static async findAll(): Promise<NetworkTargetDoc[]> {
|
|
46
|
+
return await NetworkTargetDoc.getInstances({});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { DcRouterDb } from '../classes.dcrouter-db.js';
|
|
3
|
+
import type { IRouteSecurity } from '../../../ts_interfaces/data/route-management.js';
|
|
4
|
+
|
|
5
|
+
const getDb = () => DcRouterDb.getInstance().getDb();
|
|
6
|
+
|
|
7
|
+
@plugins.smartdata.Collection(() => getDb())
|
|
8
|
+
export class SecurityProfileDoc extends plugins.smartdata.SmartDataDbDoc<SecurityProfileDoc, SecurityProfileDoc> {
|
|
9
|
+
@plugins.smartdata.unI()
|
|
10
|
+
@plugins.smartdata.svDb()
|
|
11
|
+
public id!: string;
|
|
12
|
+
|
|
13
|
+
@plugins.smartdata.svDb()
|
|
14
|
+
public name: string = '';
|
|
15
|
+
|
|
16
|
+
@plugins.smartdata.svDb()
|
|
17
|
+
public description?: string;
|
|
18
|
+
|
|
19
|
+
@plugins.smartdata.svDb()
|
|
20
|
+
public security!: IRouteSecurity;
|
|
21
|
+
|
|
22
|
+
@plugins.smartdata.svDb()
|
|
23
|
+
public extendsProfiles?: string[];
|
|
24
|
+
|
|
25
|
+
@plugins.smartdata.svDb()
|
|
26
|
+
public createdAt!: number;
|
|
27
|
+
|
|
28
|
+
@plugins.smartdata.svDb()
|
|
29
|
+
public updatedAt!: number;
|
|
30
|
+
|
|
31
|
+
@plugins.smartdata.svDb()
|
|
32
|
+
public createdBy!: string;
|
|
33
|
+
|
|
34
|
+
constructor() {
|
|
35
|
+
super();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public static async findById(id: string): Promise<SecurityProfileDoc | null> {
|
|
39
|
+
return await SecurityProfileDoc.getInstance({ id });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public static async findByName(name: string): Promise<SecurityProfileDoc | null> {
|
|
43
|
+
return await SecurityProfileDoc.getInstance({ name });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public static async findAll(): Promise<SecurityProfileDoc[]> {
|
|
47
|
+
return await SecurityProfileDoc.getInstances({});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import { DcRouterDb } from '../classes.dcrouter-db.js';
|
|
3
|
+
import type { IRouteMetadata } from '../../../ts_interfaces/data/route-management.js';
|
|
3
4
|
|
|
4
5
|
const getDb = () => DcRouterDb.getInstance().getDb();
|
|
5
6
|
|
|
@@ -24,6 +25,9 @@ export class StoredRouteDoc extends plugins.smartdata.SmartDataDbDoc<StoredRoute
|
|
|
24
25
|
@plugins.smartdata.svDb()
|
|
25
26
|
public createdBy!: string;
|
|
26
27
|
|
|
28
|
+
@plugins.smartdata.svDb()
|
|
29
|
+
public metadata?: IRouteMetadata;
|
|
30
|
+
|
|
27
31
|
constructor() {
|
|
28
32
|
super();
|
|
29
33
|
}
|
package/ts/db/documents/index.ts
CHANGED
|
@@ -6,6 +6,8 @@ export * from './classes.cached.ip.reputation.js';
|
|
|
6
6
|
export * from './classes.stored-route.doc.js';
|
|
7
7
|
export * from './classes.route-override.doc.js';
|
|
8
8
|
export * from './classes.api-token.doc.js';
|
|
9
|
+
export * from './classes.security-profile.doc.js';
|
|
10
|
+
export * from './classes.network-target.doc.js';
|
|
9
11
|
|
|
10
12
|
// VPN document classes
|
|
11
13
|
export * from './classes.vpn-server-keys.doc.js';
|
|
@@ -29,6 +29,8 @@ export class OpsServer {
|
|
|
29
29
|
private routeManagementHandler!: handlers.RouteManagementHandler;
|
|
30
30
|
private apiTokenHandler!: handlers.ApiTokenHandler;
|
|
31
31
|
private vpnHandler!: handlers.VpnHandler;
|
|
32
|
+
private securityProfileHandler!: handlers.SecurityProfileHandler;
|
|
33
|
+
private networkTargetHandler!: handlers.NetworkTargetHandler;
|
|
32
34
|
|
|
33
35
|
constructor(dcRouterRefArg: DcRouter) {
|
|
34
36
|
this.dcRouterRef = dcRouterRefArg;
|
|
@@ -88,6 +90,8 @@ export class OpsServer {
|
|
|
88
90
|
this.routeManagementHandler = new handlers.RouteManagementHandler(this);
|
|
89
91
|
this.apiTokenHandler = new handlers.ApiTokenHandler(this);
|
|
90
92
|
this.vpnHandler = new handlers.VpnHandler(this);
|
|
93
|
+
this.securityProfileHandler = new handlers.SecurityProfileHandler(this);
|
|
94
|
+
this.networkTargetHandler = new handlers.NetworkTargetHandler(this);
|
|
91
95
|
|
|
92
96
|
console.log('✅ OpsServer TypedRequest handlers initialized');
|
|
93
97
|
}
|
|
@@ -9,4 +9,6 @@ export * from './certificate.handler.js';
|
|
|
9
9
|
export * from './remoteingress.handler.js';
|
|
10
10
|
export * from './route-management.handler.js';
|
|
11
11
|
export * from './api-token.handler.js';
|
|
12
|
-
export * from './vpn.handler.js';
|
|
12
|
+
export * from './vpn.handler.js';
|
|
13
|
+
export * from './security-profile.handler.js';
|
|
14
|
+
export * from './network-target.handler.js';
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import type { OpsServer } from '../classes.opsserver.js';
|
|
3
|
+
import * as interfaces from '../../../ts_interfaces/index.js';
|
|
4
|
+
|
|
5
|
+
export class NetworkTargetHandler {
|
|
6
|
+
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
7
|
+
|
|
8
|
+
constructor(private opsServerRef: OpsServer) {
|
|
9
|
+
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
|
10
|
+
this.registerHandlers();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private async requireAuth(
|
|
14
|
+
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
|
15
|
+
requiredScope?: interfaces.data.TApiTokenScope,
|
|
16
|
+
): Promise<string> {
|
|
17
|
+
if (request.identity?.jwt) {
|
|
18
|
+
try {
|
|
19
|
+
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
|
20
|
+
identity: request.identity,
|
|
21
|
+
});
|
|
22
|
+
if (isAdmin) return request.identity.userId;
|
|
23
|
+
} catch { /* fall through */ }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (request.apiToken) {
|
|
27
|
+
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
28
|
+
if (tokenManager) {
|
|
29
|
+
const token = await tokenManager.validateToken(request.apiToken);
|
|
30
|
+
if (token) {
|
|
31
|
+
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
32
|
+
return token.createdBy;
|
|
33
|
+
}
|
|
34
|
+
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private registerHandlers(): void {
|
|
43
|
+
// Get all network targets
|
|
44
|
+
this.typedrouter.addTypedHandler(
|
|
45
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetNetworkTargets>(
|
|
46
|
+
'getNetworkTargets',
|
|
47
|
+
async (dataArg) => {
|
|
48
|
+
await this.requireAuth(dataArg, 'targets:read');
|
|
49
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
50
|
+
if (!resolver) {
|
|
51
|
+
return { targets: [] };
|
|
52
|
+
}
|
|
53
|
+
return { targets: resolver.listTargets() };
|
|
54
|
+
},
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Get a single network target
|
|
59
|
+
this.typedrouter.addTypedHandler(
|
|
60
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetNetworkTarget>(
|
|
61
|
+
'getNetworkTarget',
|
|
62
|
+
async (dataArg) => {
|
|
63
|
+
await this.requireAuth(dataArg, 'targets:read');
|
|
64
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
65
|
+
if (!resolver) {
|
|
66
|
+
return { target: null };
|
|
67
|
+
}
|
|
68
|
+
return { target: resolver.getTarget(dataArg.id) || null };
|
|
69
|
+
},
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Create a network target
|
|
74
|
+
this.typedrouter.addTypedHandler(
|
|
75
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateNetworkTarget>(
|
|
76
|
+
'createNetworkTarget',
|
|
77
|
+
async (dataArg) => {
|
|
78
|
+
const userId = await this.requireAuth(dataArg, 'targets:write');
|
|
79
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
80
|
+
if (!resolver) {
|
|
81
|
+
return { success: false, message: 'Reference resolver not initialized' };
|
|
82
|
+
}
|
|
83
|
+
const id = await resolver.createTarget({
|
|
84
|
+
name: dataArg.name,
|
|
85
|
+
description: dataArg.description,
|
|
86
|
+
host: dataArg.host,
|
|
87
|
+
port: dataArg.port,
|
|
88
|
+
createdBy: userId,
|
|
89
|
+
});
|
|
90
|
+
return { success: true, id };
|
|
91
|
+
},
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Update a network target
|
|
96
|
+
this.typedrouter.addTypedHandler(
|
|
97
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateNetworkTarget>(
|
|
98
|
+
'updateNetworkTarget',
|
|
99
|
+
async (dataArg) => {
|
|
100
|
+
await this.requireAuth(dataArg, 'targets:write');
|
|
101
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
102
|
+
const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
|
|
103
|
+
if (!resolver || !manager) {
|
|
104
|
+
return { success: false, message: 'Not initialized' };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { affectedRouteIds } = await resolver.updateTarget(dataArg.id, {
|
|
108
|
+
name: dataArg.name,
|
|
109
|
+
description: dataArg.description,
|
|
110
|
+
host: dataArg.host,
|
|
111
|
+
port: dataArg.port,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (affectedRouteIds.length > 0) {
|
|
115
|
+
await manager.reResolveRoutes(affectedRouteIds);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return { success: true, affectedRouteCount: affectedRouteIds.length };
|
|
119
|
+
},
|
|
120
|
+
),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Delete a network target
|
|
124
|
+
this.typedrouter.addTypedHandler(
|
|
125
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteNetworkTarget>(
|
|
126
|
+
'deleteNetworkTarget',
|
|
127
|
+
async (dataArg) => {
|
|
128
|
+
await this.requireAuth(dataArg, 'targets:write');
|
|
129
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
130
|
+
const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
|
|
131
|
+
if (!resolver || !manager) {
|
|
132
|
+
return { success: false, message: 'Not initialized' };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const result = await resolver.deleteTarget(
|
|
136
|
+
dataArg.id,
|
|
137
|
+
dataArg.force ?? false,
|
|
138
|
+
manager.getStoredRoutes(),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (result.success && dataArg.force) {
|
|
142
|
+
await manager.applyRoutes();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return result;
|
|
146
|
+
},
|
|
147
|
+
),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// Get routes using a network target
|
|
151
|
+
this.typedrouter.addTypedHandler(
|
|
152
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetNetworkTargetUsage>(
|
|
153
|
+
'getNetworkTargetUsage',
|
|
154
|
+
async (dataArg) => {
|
|
155
|
+
await this.requireAuth(dataArg, 'targets:read');
|
|
156
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
157
|
+
const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
|
|
158
|
+
if (!resolver || !manager) {
|
|
159
|
+
return { routes: [] };
|
|
160
|
+
}
|
|
161
|
+
const usage = resolver.getTargetUsageForId(dataArg.id, manager.getStoredRoutes());
|
|
162
|
+
return { routes: usage.map((u) => ({ id: u.id, name: u.routeName })) };
|
|
163
|
+
},
|
|
164
|
+
),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -71,7 +71,7 @@ export class RouteManagementHandler {
|
|
|
71
71
|
if (!manager) {
|
|
72
72
|
return { success: false, message: 'Route management not initialized' };
|
|
73
73
|
}
|
|
74
|
-
const id = await manager.createRoute(dataArg.route, userId, dataArg.enabled ?? true);
|
|
74
|
+
const id = await manager.createRoute(dataArg.route, userId, dataArg.enabled ?? true, dataArg.metadata);
|
|
75
75
|
return { success: true, storedRouteId: id };
|
|
76
76
|
},
|
|
77
77
|
),
|
|
@@ -90,6 +90,7 @@ export class RouteManagementHandler {
|
|
|
90
90
|
const ok = await manager.updateRoute(dataArg.id, {
|
|
91
91
|
route: dataArg.route as any,
|
|
92
92
|
enabled: dataArg.enabled,
|
|
93
|
+
metadata: dataArg.metadata,
|
|
93
94
|
});
|
|
94
95
|
return { success: ok, message: ok ? undefined : 'Route not found' };
|
|
95
96
|
},
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import type { OpsServer } from '../classes.opsserver.js';
|
|
3
|
+
import * as interfaces from '../../../ts_interfaces/index.js';
|
|
4
|
+
|
|
5
|
+
export class SecurityProfileHandler {
|
|
6
|
+
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
7
|
+
|
|
8
|
+
constructor(private opsServerRef: OpsServer) {
|
|
9
|
+
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
|
10
|
+
this.registerHandlers();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private async requireAuth(
|
|
14
|
+
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
|
15
|
+
requiredScope?: interfaces.data.TApiTokenScope,
|
|
16
|
+
): Promise<string> {
|
|
17
|
+
if (request.identity?.jwt) {
|
|
18
|
+
try {
|
|
19
|
+
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
|
20
|
+
identity: request.identity,
|
|
21
|
+
});
|
|
22
|
+
if (isAdmin) return request.identity.userId;
|
|
23
|
+
} catch { /* fall through */ }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (request.apiToken) {
|
|
27
|
+
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
28
|
+
if (tokenManager) {
|
|
29
|
+
const token = await tokenManager.validateToken(request.apiToken);
|
|
30
|
+
if (token) {
|
|
31
|
+
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
32
|
+
return token.createdBy;
|
|
33
|
+
}
|
|
34
|
+
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private registerHandlers(): void {
|
|
43
|
+
// Get all security profiles
|
|
44
|
+
this.typedrouter.addTypedHandler(
|
|
45
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSecurityProfiles>(
|
|
46
|
+
'getSecurityProfiles',
|
|
47
|
+
async (dataArg) => {
|
|
48
|
+
await this.requireAuth(dataArg, 'profiles:read');
|
|
49
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
50
|
+
if (!resolver) {
|
|
51
|
+
return { profiles: [] };
|
|
52
|
+
}
|
|
53
|
+
return { profiles: resolver.listProfiles() };
|
|
54
|
+
},
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Get a single security profile
|
|
59
|
+
this.typedrouter.addTypedHandler(
|
|
60
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSecurityProfile>(
|
|
61
|
+
'getSecurityProfile',
|
|
62
|
+
async (dataArg) => {
|
|
63
|
+
await this.requireAuth(dataArg, 'profiles:read');
|
|
64
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
65
|
+
if (!resolver) {
|
|
66
|
+
return { profile: null };
|
|
67
|
+
}
|
|
68
|
+
return { profile: resolver.getProfile(dataArg.id) || null };
|
|
69
|
+
},
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Create a security profile
|
|
74
|
+
this.typedrouter.addTypedHandler(
|
|
75
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateSecurityProfile>(
|
|
76
|
+
'createSecurityProfile',
|
|
77
|
+
async (dataArg) => {
|
|
78
|
+
const userId = await this.requireAuth(dataArg, 'profiles:write');
|
|
79
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
80
|
+
if (!resolver) {
|
|
81
|
+
return { success: false, message: 'Reference resolver not initialized' };
|
|
82
|
+
}
|
|
83
|
+
const id = await resolver.createProfile({
|
|
84
|
+
name: dataArg.name,
|
|
85
|
+
description: dataArg.description,
|
|
86
|
+
security: dataArg.security,
|
|
87
|
+
extendsProfiles: dataArg.extendsProfiles,
|
|
88
|
+
createdBy: userId,
|
|
89
|
+
});
|
|
90
|
+
return { success: true, id };
|
|
91
|
+
},
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Update a security profile
|
|
96
|
+
this.typedrouter.addTypedHandler(
|
|
97
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateSecurityProfile>(
|
|
98
|
+
'updateSecurityProfile',
|
|
99
|
+
async (dataArg) => {
|
|
100
|
+
await this.requireAuth(dataArg, 'profiles:write');
|
|
101
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
102
|
+
const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
|
|
103
|
+
if (!resolver || !manager) {
|
|
104
|
+
return { success: false, message: 'Not initialized' };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { affectedRouteIds } = await resolver.updateProfile(dataArg.id, {
|
|
108
|
+
name: dataArg.name,
|
|
109
|
+
description: dataArg.description,
|
|
110
|
+
security: dataArg.security,
|
|
111
|
+
extendsProfiles: dataArg.extendsProfiles,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Propagate to affected routes
|
|
115
|
+
if (affectedRouteIds.length > 0) {
|
|
116
|
+
await manager.reResolveRoutes(affectedRouteIds);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { success: true, affectedRouteCount: affectedRouteIds.length };
|
|
120
|
+
},
|
|
121
|
+
),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Delete a security profile
|
|
125
|
+
this.typedrouter.addTypedHandler(
|
|
126
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteSecurityProfile>(
|
|
127
|
+
'deleteSecurityProfile',
|
|
128
|
+
async (dataArg) => {
|
|
129
|
+
await this.requireAuth(dataArg, 'profiles:write');
|
|
130
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
131
|
+
const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
|
|
132
|
+
if (!resolver || !manager) {
|
|
133
|
+
return { success: false, message: 'Not initialized' };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const result = await resolver.deleteProfile(
|
|
137
|
+
dataArg.id,
|
|
138
|
+
dataArg.force ?? false,
|
|
139
|
+
manager.getStoredRoutes(),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// If force-deleted with affected routes, re-apply
|
|
143
|
+
if (result.success && dataArg.force) {
|
|
144
|
+
await manager.applyRoutes();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return result;
|
|
148
|
+
},
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Get routes using a security profile
|
|
153
|
+
this.typedrouter.addTypedHandler(
|
|
154
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSecurityProfileUsage>(
|
|
155
|
+
'getSecurityProfileUsage',
|
|
156
|
+
async (dataArg) => {
|
|
157
|
+
await this.requireAuth(dataArg, 'profiles:read');
|
|
158
|
+
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
159
|
+
const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
|
|
160
|
+
if (!resolver || !manager) {
|
|
161
|
+
return { routes: [] };
|
|
162
|
+
}
|
|
163
|
+
const usage = resolver.getProfileUsageForId(dataArg.id, manager.getStoredRoutes());
|
|
164
|
+
return { routes: usage.map((u) => ({ id: u.id, name: u.routeName })) };
|
|
165
|
+
},
|
|
166
|
+
),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|