@serve.zone/dcrouter 12.10.0 → 13.0.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 +957 -866
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.dcrouter.d.ts +5 -4
- package/dist_ts/classes.dcrouter.js +25 -49
- package/dist_ts/config/classes.reference-resolver.d.ts +7 -7
- package/dist_ts/config/classes.reference-resolver.js +27 -27
- package/dist_ts/config/classes.route-config-manager.d.ts +2 -2
- package/dist_ts/config/classes.route-config-manager.js +12 -15
- package/dist_ts/config/classes.target-profile-manager.d.ts +63 -0
- package/dist_ts/config/classes.target-profile-manager.js +295 -0
- package/dist_ts/config/index.d.ts +1 -0
- package/dist_ts/config/index.js +2 -1
- package/dist_ts/db/documents/{classes.security-profile.doc.d.ts → classes.source-profile.doc.d.ts} +4 -4
- package/dist_ts/db/documents/{classes.security-profile.doc.js → classes.source-profile.doc.js} +9 -9
- package/dist_ts/db/documents/classes.target-profile.doc.d.ts +17 -0
- package/dist_ts/db/documents/classes.target-profile.doc.js +124 -0
- package/dist_ts/db/documents/classes.vpn-client.doc.d.ts +1 -1
- package/dist_ts/db/documents/classes.vpn-client.doc.js +8 -8
- package/dist_ts/db/documents/index.d.ts +2 -1
- package/dist_ts/db/documents/index.js +3 -2
- package/dist_ts/opsserver/classes.opsserver.d.ts +2 -1
- package/dist_ts/opsserver/classes.opsserver.js +5 -3
- package/dist_ts/opsserver/handlers/index.d.ts +2 -1
- package/dist_ts/opsserver/handlers/index.js +3 -2
- package/dist_ts/opsserver/handlers/{security-profile.handler.d.ts → source-profile.handler.d.ts} +1 -1
- package/dist_ts/opsserver/handlers/{security-profile.handler.js → source-profile.handler.js} +20 -20
- package/dist_ts/opsserver/handlers/target-profile.handler.d.ts +10 -0
- package/dist_ts/opsserver/handlers/target-profile.handler.js +115 -0
- package/dist_ts/opsserver/handlers/vpn.handler.js +5 -5
- package/dist_ts/vpn/classes.vpn-manager.d.ts +6 -10
- package/dist_ts/vpn/classes.vpn-manager.js +11 -34
- package/dist_ts_interfaces/data/index.d.ts +1 -0
- package/dist_ts_interfaces/data/index.js +2 -1
- package/dist_ts_interfaces/data/remoteingress.d.ts +4 -15
- package/dist_ts_interfaces/data/route-management.d.ts +9 -6
- package/dist_ts_interfaces/data/target-profile.d.ts +28 -0
- package/dist_ts_interfaces/data/target-profile.js +2 -0
- package/dist_ts_interfaces/data/vpn.d.ts +2 -1
- package/dist_ts_interfaces/requests/index.d.ts +2 -1
- package/dist_ts_interfaces/requests/index.js +3 -2
- package/dist_ts_interfaces/requests/{security-profiles.d.ts → source-profiles.d.ts} +21 -21
- package/dist_ts_interfaces/requests/source-profiles.js +2 -0
- package/dist_ts_interfaces/requests/target-profiles.d.ts +103 -0
- package/dist_ts_interfaces/requests/target-profiles.js +2 -0
- package/dist_ts_interfaces/requests/vpn.d.ts +2 -2
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/dist_ts_web/appstate.d.ts +36 -3
- package/dist_ts_web/appstate.js +127 -10
- package/dist_ts_web/elements/index.d.ts +2 -1
- package/dist_ts_web/elements/index.js +3 -2
- package/dist_ts_web/elements/ops-dashboard.js +10 -4
- package/dist_ts_web/elements/ops-view-routes.js +8 -8
- package/dist_ts_web/elements/{ops-view-securityprofiles.d.ts → ops-view-sourceprofiles.d.ts} +2 -2
- package/dist_ts_web/elements/{ops-view-securityprofiles.js → ops-view-sourceprofiles.js} +12 -12
- package/dist_ts_web/elements/ops-view-targetprofiles.d.ts +19 -0
- package/dist_ts_web/elements/ops-view-targetprofiles.js +412 -0
- package/dist_ts_web/elements/ops-view-vpn.js +13 -13
- 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 +33 -50
- package/ts/config/classes.reference-resolver.ts +34 -34
- package/ts/config/classes.route-config-manager.ts +9 -12
- package/ts/config/classes.target-profile-manager.ts +348 -0
- package/ts/config/index.ts +2 -1
- package/ts/db/documents/{classes.security-profile.doc.ts → classes.source-profile.doc.ts} +7 -7
- package/ts/db/documents/classes.target-profile.doc.ts +52 -0
- package/ts/db/documents/classes.vpn-client.doc.ts +1 -1
- package/ts/db/documents/index.ts +2 -1
- package/ts/opsserver/classes.opsserver.ts +4 -2
- package/ts/opsserver/handlers/index.ts +2 -1
- package/ts/opsserver/handlers/{security-profile.handler.ts → source-profile.handler.ts} +25 -25
- package/ts/opsserver/handlers/target-profile.handler.ts +155 -0
- package/ts/opsserver/handlers/vpn.handler.ts +4 -4
- package/ts/vpn/classes.vpn-manager.ts +14 -38
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +180 -17
- package/ts_web/elements/index.ts +2 -1
- package/ts_web/elements/ops-dashboard.ts +9 -3
- package/ts_web/elements/ops-view-routes.ts +7 -7
- package/ts_web/elements/{ops-view-securityprofiles.ts → ops-view-sourceprofiles.ts} +13 -13
- package/ts_web/elements/ops-view-targetprofiles.ts +379 -0
- package/ts_web/elements/ops-view-vpn.ts +12 -12
- package/ts_web/router.ts +1 -1
- package/dist_ts_interfaces/requests/security-profiles.js +0 -2
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { DcRouterDb } from '../classes.dcrouter-db.js';
|
|
3
|
+
import type { ITargetProfileTarget } from '../../../ts_interfaces/data/target-profile.js';
|
|
4
|
+
|
|
5
|
+
const getDb = () => DcRouterDb.getInstance().getDb();
|
|
6
|
+
|
|
7
|
+
@plugins.smartdata.Collection(() => getDb())
|
|
8
|
+
export class TargetProfileDoc extends plugins.smartdata.SmartDataDbDoc<TargetProfileDoc, TargetProfileDoc> {
|
|
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 domains?: string[];
|
|
21
|
+
|
|
22
|
+
@plugins.smartdata.svDb()
|
|
23
|
+
public targets?: ITargetProfileTarget[];
|
|
24
|
+
|
|
25
|
+
@plugins.smartdata.svDb()
|
|
26
|
+
public routeRefs?: string[];
|
|
27
|
+
|
|
28
|
+
@plugins.smartdata.svDb()
|
|
29
|
+
public createdAt!: number;
|
|
30
|
+
|
|
31
|
+
@plugins.smartdata.svDb()
|
|
32
|
+
public updatedAt!: number;
|
|
33
|
+
|
|
34
|
+
@plugins.smartdata.svDb()
|
|
35
|
+
public createdBy!: string;
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
super();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public static async findById(id: string): Promise<TargetProfileDoc | null> {
|
|
42
|
+
return await TargetProfileDoc.getInstance({ id });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public static async findByName(name: string): Promise<TargetProfileDoc | null> {
|
|
46
|
+
return await TargetProfileDoc.getInstance({ name });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public static async findAll(): Promise<TargetProfileDoc[]> {
|
|
50
|
+
return await TargetProfileDoc.getInstances({});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -13,7 +13,7 @@ export class VpnClientDoc extends plugins.smartdata.SmartDataDbDoc<VpnClientDoc,
|
|
|
13
13
|
public enabled!: boolean;
|
|
14
14
|
|
|
15
15
|
@plugins.smartdata.svDb()
|
|
16
|
-
public
|
|
16
|
+
public targetProfileIds?: string[];
|
|
17
17
|
|
|
18
18
|
@plugins.smartdata.svDb()
|
|
19
19
|
public description?: string;
|
package/ts/db/documents/index.ts
CHANGED
|
@@ -6,7 +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.
|
|
9
|
+
export * from './classes.source-profile.doc.js';
|
|
10
|
+
export * from './classes.target-profile.doc.js';
|
|
10
11
|
export * from './classes.network-target.doc.js';
|
|
11
12
|
|
|
12
13
|
// VPN document classes
|
|
@@ -29,7 +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
|
|
32
|
+
private sourceProfileHandler!: handlers.SourceProfileHandler;
|
|
33
|
+
private targetProfileHandler!: handlers.TargetProfileHandler;
|
|
33
34
|
private networkTargetHandler!: handlers.NetworkTargetHandler;
|
|
34
35
|
|
|
35
36
|
constructor(dcRouterRefArg: DcRouter) {
|
|
@@ -90,7 +91,8 @@ export class OpsServer {
|
|
|
90
91
|
this.routeManagementHandler = new handlers.RouteManagementHandler(this);
|
|
91
92
|
this.apiTokenHandler = new handlers.ApiTokenHandler(this);
|
|
92
93
|
this.vpnHandler = new handlers.VpnHandler(this);
|
|
93
|
-
this.
|
|
94
|
+
this.sourceProfileHandler = new handlers.SourceProfileHandler(this);
|
|
95
|
+
this.targetProfileHandler = new handlers.TargetProfileHandler(this);
|
|
94
96
|
this.networkTargetHandler = new handlers.NetworkTargetHandler(this);
|
|
95
97
|
|
|
96
98
|
console.log('✅ OpsServer TypedRequest handlers initialized');
|
|
@@ -10,5 +10,6 @@ export * from './remoteingress.handler.js';
|
|
|
10
10
|
export * from './route-management.handler.js';
|
|
11
11
|
export * from './api-token.handler.js';
|
|
12
12
|
export * from './vpn.handler.js';
|
|
13
|
-
export * from './
|
|
13
|
+
export * from './source-profile.handler.js';
|
|
14
|
+
export * from './target-profile.handler.js';
|
|
14
15
|
export * from './network-target.handler.js';
|
|
@@ -2,7 +2,7 @@ import * as plugins from '../../plugins.js';
|
|
|
2
2
|
import type { OpsServer } from '../classes.opsserver.js';
|
|
3
3
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
|
4
4
|
|
|
5
|
-
export class
|
|
5
|
+
export class SourceProfileHandler {
|
|
6
6
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
7
7
|
|
|
8
8
|
constructor(private opsServerRef: OpsServer) {
|
|
@@ -40,12 +40,12 @@ export class SecurityProfileHandler {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
private registerHandlers(): void {
|
|
43
|
-
// Get all
|
|
43
|
+
// Get all source profiles
|
|
44
44
|
this.typedrouter.addTypedHandler(
|
|
45
|
-
new plugins.typedrequest.TypedHandler<interfaces.requests.
|
|
46
|
-
'
|
|
45
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSourceProfiles>(
|
|
46
|
+
'getSourceProfiles',
|
|
47
47
|
async (dataArg) => {
|
|
48
|
-
await this.requireAuth(dataArg, 'profiles:read');
|
|
48
|
+
await this.requireAuth(dataArg, 'source-profiles:read');
|
|
49
49
|
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
50
50
|
if (!resolver) {
|
|
51
51
|
return { profiles: [] };
|
|
@@ -55,12 +55,12 @@ export class SecurityProfileHandler {
|
|
|
55
55
|
),
|
|
56
56
|
);
|
|
57
57
|
|
|
58
|
-
// Get a single
|
|
58
|
+
// Get a single source profile
|
|
59
59
|
this.typedrouter.addTypedHandler(
|
|
60
|
-
new plugins.typedrequest.TypedHandler<interfaces.requests.
|
|
61
|
-
'
|
|
60
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSourceProfile>(
|
|
61
|
+
'getSourceProfile',
|
|
62
62
|
async (dataArg) => {
|
|
63
|
-
await this.requireAuth(dataArg, 'profiles:read');
|
|
63
|
+
await this.requireAuth(dataArg, 'source-profiles:read');
|
|
64
64
|
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
65
65
|
if (!resolver) {
|
|
66
66
|
return { profile: null };
|
|
@@ -70,12 +70,12 @@ export class SecurityProfileHandler {
|
|
|
70
70
|
),
|
|
71
71
|
);
|
|
72
72
|
|
|
73
|
-
// Create a
|
|
73
|
+
// Create a source profile
|
|
74
74
|
this.typedrouter.addTypedHandler(
|
|
75
|
-
new plugins.typedrequest.TypedHandler<interfaces.requests.
|
|
76
|
-
'
|
|
75
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateSourceProfile>(
|
|
76
|
+
'createSourceProfile',
|
|
77
77
|
async (dataArg) => {
|
|
78
|
-
const userId = await this.requireAuth(dataArg, 'profiles:write');
|
|
78
|
+
const userId = await this.requireAuth(dataArg, 'source-profiles:write');
|
|
79
79
|
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
80
80
|
if (!resolver) {
|
|
81
81
|
return { success: false, message: 'Reference resolver not initialized' };
|
|
@@ -92,12 +92,12 @@ export class SecurityProfileHandler {
|
|
|
92
92
|
),
|
|
93
93
|
);
|
|
94
94
|
|
|
95
|
-
// Update a
|
|
95
|
+
// Update a source profile
|
|
96
96
|
this.typedrouter.addTypedHandler(
|
|
97
|
-
new plugins.typedrequest.TypedHandler<interfaces.requests.
|
|
98
|
-
'
|
|
97
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateSourceProfile>(
|
|
98
|
+
'updateSourceProfile',
|
|
99
99
|
async (dataArg) => {
|
|
100
|
-
await this.requireAuth(dataArg, 'profiles:write');
|
|
100
|
+
await this.requireAuth(dataArg, 'source-profiles:write');
|
|
101
101
|
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
102
102
|
const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
|
|
103
103
|
if (!resolver || !manager) {
|
|
@@ -121,12 +121,12 @@ export class SecurityProfileHandler {
|
|
|
121
121
|
),
|
|
122
122
|
);
|
|
123
123
|
|
|
124
|
-
// Delete a
|
|
124
|
+
// Delete a source profile
|
|
125
125
|
this.typedrouter.addTypedHandler(
|
|
126
|
-
new plugins.typedrequest.TypedHandler<interfaces.requests.
|
|
127
|
-
'
|
|
126
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteSourceProfile>(
|
|
127
|
+
'deleteSourceProfile',
|
|
128
128
|
async (dataArg) => {
|
|
129
|
-
await this.requireAuth(dataArg, 'profiles:write');
|
|
129
|
+
await this.requireAuth(dataArg, 'source-profiles:write');
|
|
130
130
|
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
131
131
|
const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
|
|
132
132
|
if (!resolver || !manager) {
|
|
@@ -149,12 +149,12 @@ export class SecurityProfileHandler {
|
|
|
149
149
|
),
|
|
150
150
|
);
|
|
151
151
|
|
|
152
|
-
// Get routes using a
|
|
152
|
+
// Get routes using a source profile
|
|
153
153
|
this.typedrouter.addTypedHandler(
|
|
154
|
-
new plugins.typedrequest.TypedHandler<interfaces.requests.
|
|
155
|
-
'
|
|
154
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSourceProfileUsage>(
|
|
155
|
+
'getSourceProfileUsage',
|
|
156
156
|
async (dataArg) => {
|
|
157
|
-
await this.requireAuth(dataArg, 'profiles:read');
|
|
157
|
+
await this.requireAuth(dataArg, 'source-profiles:read');
|
|
158
158
|
const resolver = this.opsServerRef.dcRouterRef.referenceResolver;
|
|
159
159
|
const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
|
|
160
160
|
if (!resolver || !manager) {
|
|
@@ -0,0 +1,155 @@
|
|
|
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 TargetProfileHandler {
|
|
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 target profiles
|
|
44
|
+
this.typedrouter.addTypedHandler(
|
|
45
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetTargetProfiles>(
|
|
46
|
+
'getTargetProfiles',
|
|
47
|
+
async (dataArg) => {
|
|
48
|
+
await this.requireAuth(dataArg, 'target-profiles:read');
|
|
49
|
+
const manager = this.opsServerRef.dcRouterRef.targetProfileManager;
|
|
50
|
+
if (!manager) {
|
|
51
|
+
return { profiles: [] };
|
|
52
|
+
}
|
|
53
|
+
return { profiles: manager.listProfiles() };
|
|
54
|
+
},
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Get a single target profile
|
|
59
|
+
this.typedrouter.addTypedHandler(
|
|
60
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetTargetProfile>(
|
|
61
|
+
'getTargetProfile',
|
|
62
|
+
async (dataArg) => {
|
|
63
|
+
await this.requireAuth(dataArg, 'target-profiles:read');
|
|
64
|
+
const manager = this.opsServerRef.dcRouterRef.targetProfileManager;
|
|
65
|
+
if (!manager) {
|
|
66
|
+
return { profile: null };
|
|
67
|
+
}
|
|
68
|
+
return { profile: manager.getProfile(dataArg.id) || null };
|
|
69
|
+
},
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Create a target profile
|
|
74
|
+
this.typedrouter.addTypedHandler(
|
|
75
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateTargetProfile>(
|
|
76
|
+
'createTargetProfile',
|
|
77
|
+
async (dataArg) => {
|
|
78
|
+
const userId = await this.requireAuth(dataArg, 'target-profiles:write');
|
|
79
|
+
const manager = this.opsServerRef.dcRouterRef.targetProfileManager;
|
|
80
|
+
if (!manager) {
|
|
81
|
+
return { success: false, message: 'Target profile manager not initialized' };
|
|
82
|
+
}
|
|
83
|
+
const id = await manager.createProfile({
|
|
84
|
+
name: dataArg.name,
|
|
85
|
+
description: dataArg.description,
|
|
86
|
+
domains: dataArg.domains,
|
|
87
|
+
targets: dataArg.targets,
|
|
88
|
+
routeRefs: dataArg.routeRefs,
|
|
89
|
+
createdBy: userId,
|
|
90
|
+
});
|
|
91
|
+
return { success: true, id };
|
|
92
|
+
},
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Update a target profile
|
|
97
|
+
this.typedrouter.addTypedHandler(
|
|
98
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateTargetProfile>(
|
|
99
|
+
'updateTargetProfile',
|
|
100
|
+
async (dataArg) => {
|
|
101
|
+
await this.requireAuth(dataArg, 'target-profiles:write');
|
|
102
|
+
const manager = this.opsServerRef.dcRouterRef.targetProfileManager;
|
|
103
|
+
if (!manager) {
|
|
104
|
+
return { success: false, message: 'Not initialized' };
|
|
105
|
+
}
|
|
106
|
+
await manager.updateProfile(dataArg.id, {
|
|
107
|
+
name: dataArg.name,
|
|
108
|
+
description: dataArg.description,
|
|
109
|
+
domains: dataArg.domains,
|
|
110
|
+
targets: dataArg.targets,
|
|
111
|
+
routeRefs: dataArg.routeRefs,
|
|
112
|
+
});
|
|
113
|
+
// Re-apply routes to update VPN access
|
|
114
|
+
this.opsServerRef.dcRouterRef.routeConfigManager?.applyRoutes();
|
|
115
|
+
return { success: true };
|
|
116
|
+
},
|
|
117
|
+
),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Delete a target profile
|
|
121
|
+
this.typedrouter.addTypedHandler(
|
|
122
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteTargetProfile>(
|
|
123
|
+
'deleteTargetProfile',
|
|
124
|
+
async (dataArg) => {
|
|
125
|
+
await this.requireAuth(dataArg, 'target-profiles:write');
|
|
126
|
+
const manager = this.opsServerRef.dcRouterRef.targetProfileManager;
|
|
127
|
+
if (!manager) {
|
|
128
|
+
return { success: false, message: 'Not initialized' };
|
|
129
|
+
}
|
|
130
|
+
const result = await manager.deleteProfile(dataArg.id, dataArg.force);
|
|
131
|
+
if (result.success) {
|
|
132
|
+
// Re-apply routes to update VPN access
|
|
133
|
+
this.opsServerRef.dcRouterRef.routeConfigManager?.applyRoutes();
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
},
|
|
137
|
+
),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Get VPN clients using a target profile
|
|
141
|
+
this.typedrouter.addTypedHandler(
|
|
142
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetTargetProfileUsage>(
|
|
143
|
+
'getTargetProfileUsage',
|
|
144
|
+
async (dataArg) => {
|
|
145
|
+
await this.requireAuth(dataArg, 'target-profiles:read');
|
|
146
|
+
const manager = this.opsServerRef.dcRouterRef.targetProfileManager;
|
|
147
|
+
if (!manager) {
|
|
148
|
+
return { clients: [] };
|
|
149
|
+
}
|
|
150
|
+
return { clients: await manager.getProfileUsage(dataArg.id) };
|
|
151
|
+
},
|
|
152
|
+
),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -25,7 +25,7 @@ export class VpnHandler {
|
|
|
25
25
|
const clients = manager.listClients().map((c) => ({
|
|
26
26
|
clientId: c.clientId,
|
|
27
27
|
enabled: c.enabled,
|
|
28
|
-
|
|
28
|
+
targetProfileIds: c.targetProfileIds,
|
|
29
29
|
description: c.description,
|
|
30
30
|
assignedIp: c.assignedIp,
|
|
31
31
|
createdAt: c.createdAt,
|
|
@@ -120,7 +120,7 @@ export class VpnHandler {
|
|
|
120
120
|
try {
|
|
121
121
|
const bundle = await manager.createClient({
|
|
122
122
|
clientId: dataArg.clientId,
|
|
123
|
-
|
|
123
|
+
targetProfileIds: dataArg.targetProfileIds,
|
|
124
124
|
description: dataArg.description,
|
|
125
125
|
forceDestinationSmartproxy: dataArg.forceDestinationSmartproxy,
|
|
126
126
|
destinationAllowList: dataArg.destinationAllowList,
|
|
@@ -142,7 +142,7 @@ export class VpnHandler {
|
|
|
142
142
|
client: {
|
|
143
143
|
clientId: bundle.entry.clientId,
|
|
144
144
|
enabled: bundle.entry.enabled ?? true,
|
|
145
|
-
|
|
145
|
+
targetProfileIds: persistedClient?.targetProfileIds,
|
|
146
146
|
description: bundle.entry.description,
|
|
147
147
|
assignedIp: bundle.entry.assignedIp,
|
|
148
148
|
createdAt: Date.now(),
|
|
@@ -179,7 +179,7 @@ export class VpnHandler {
|
|
|
179
179
|
try {
|
|
180
180
|
await manager.updateClient(dataArg.clientId, {
|
|
181
181
|
description: dataArg.description,
|
|
182
|
-
|
|
182
|
+
targetProfileIds: dataArg.targetProfileIds,
|
|
183
183
|
forceDestinationSmartproxy: dataArg.forceDestinationSmartproxy,
|
|
184
184
|
destinationAllowList: dataArg.destinationAllowList,
|
|
185
185
|
destinationBlockList: dataArg.destinationBlockList,
|
|
@@ -14,7 +14,7 @@ export interface IVpnManagerConfig {
|
|
|
14
14
|
/** Pre-defined VPN clients created on startup (idempotent — skips already-persisted clients) */
|
|
15
15
|
initialClients?: Array<{
|
|
16
16
|
clientId: string;
|
|
17
|
-
|
|
17
|
+
targetProfileIds?: string[];
|
|
18
18
|
description?: string;
|
|
19
19
|
}>;
|
|
20
20
|
/** Called when clients are created/deleted/toggled — triggers route re-application */
|
|
@@ -26,10 +26,10 @@ export interface IVpnManagerConfig {
|
|
|
26
26
|
allowList?: string[];
|
|
27
27
|
blockList?: string[];
|
|
28
28
|
};
|
|
29
|
-
/** Compute per-client AllowedIPs based on the client's
|
|
29
|
+
/** Compute per-client AllowedIPs based on the client's target profile IDs.
|
|
30
30
|
* Called at config generation time (create/export). Returns CIDRs for WireGuard AllowedIPs.
|
|
31
31
|
* When not set, defaults to [subnet]. */
|
|
32
|
-
getClientAllowedIPs?: (
|
|
32
|
+
getClientAllowedIPs?: (targetProfileIds: string[]) => Promise<string[]>;
|
|
33
33
|
/** Forwarding mode: 'socket' (default, userspace NAT), 'bridge' (L2 bridge to host LAN),
|
|
34
34
|
* or 'hybrid' (socket default, bridge for clients with useHostIp=true) */
|
|
35
35
|
forwardingMode?: 'socket' | 'bridge' | 'hybrid';
|
|
@@ -90,7 +90,6 @@ export class VpnManager {
|
|
|
90
90
|
publicKey: client.noisePublicKey,
|
|
91
91
|
wgPublicKey: client.wgPublicKey,
|
|
92
92
|
enabled: client.enabled,
|
|
93
|
-
serverDefinedClientTags: client.serverDefinedClientTags,
|
|
94
93
|
description: client.description,
|
|
95
94
|
assignedIp: client.assignedIp,
|
|
96
95
|
expiresAt: client.expiresAt,
|
|
@@ -163,7 +162,7 @@ export class VpnManager {
|
|
|
163
162
|
if (!this.clients.has(initial.clientId)) {
|
|
164
163
|
const bundle = await this.createClient({
|
|
165
164
|
clientId: initial.clientId,
|
|
166
|
-
|
|
165
|
+
targetProfileIds: initial.targetProfileIds,
|
|
167
166
|
description: initial.description,
|
|
168
167
|
});
|
|
169
168
|
logger.log('info', `VPN: Created initial client '${initial.clientId}' (IP: ${bundle.entry.assignedIp})`);
|
|
@@ -197,7 +196,7 @@ export class VpnManager {
|
|
|
197
196
|
*/
|
|
198
197
|
public async createClient(opts: {
|
|
199
198
|
clientId: string;
|
|
200
|
-
|
|
199
|
+
targetProfileIds?: string[];
|
|
201
200
|
description?: string;
|
|
202
201
|
forceDestinationSmartproxy?: boolean;
|
|
203
202
|
destinationAllowList?: string[];
|
|
@@ -214,13 +213,12 @@ export class VpnManager {
|
|
|
214
213
|
|
|
215
214
|
const bundle = await this.vpnServer.createClient({
|
|
216
215
|
clientId: opts.clientId,
|
|
217
|
-
serverDefinedClientTags: opts.serverDefinedClientTags,
|
|
218
216
|
description: opts.description,
|
|
219
217
|
});
|
|
220
218
|
|
|
221
|
-
// Override AllowedIPs with per-client values based on
|
|
219
|
+
// Override AllowedIPs with per-client values based on target profiles
|
|
222
220
|
if (this.config.getClientAllowedIPs && bundle.wireguardConfig) {
|
|
223
|
-
const allowedIPs = await this.config.getClientAllowedIPs(opts.
|
|
221
|
+
const allowedIPs = await this.config.getClientAllowedIPs(opts.targetProfileIds || []);
|
|
224
222
|
bundle.wireguardConfig = bundle.wireguardConfig.replace(
|
|
225
223
|
/AllowedIPs\s*=\s*.+/,
|
|
226
224
|
`AllowedIPs = ${allowedIPs.join(', ')}`,
|
|
@@ -231,7 +229,7 @@ export class VpnManager {
|
|
|
231
229
|
const doc = new VpnClientDoc();
|
|
232
230
|
doc.clientId = bundle.entry.clientId;
|
|
233
231
|
doc.enabled = bundle.entry.enabled ?? true;
|
|
234
|
-
doc.
|
|
232
|
+
doc.targetProfileIds = opts.targetProfileIds;
|
|
235
233
|
doc.description = bundle.entry.description;
|
|
236
234
|
doc.assignedIp = bundle.entry.assignedIp;
|
|
237
235
|
doc.noisePublicKey = bundle.entry.publicKey;
|
|
@@ -332,11 +330,11 @@ export class VpnManager {
|
|
|
332
330
|
}
|
|
333
331
|
|
|
334
332
|
/**
|
|
335
|
-
* Update a client's metadata (description,
|
|
333
|
+
* Update a client's metadata (description, target profiles) without rotating keys.
|
|
336
334
|
*/
|
|
337
335
|
public async updateClient(clientId: string, update: {
|
|
338
336
|
description?: string;
|
|
339
|
-
|
|
337
|
+
targetProfileIds?: string[];
|
|
340
338
|
forceDestinationSmartproxy?: boolean;
|
|
341
339
|
destinationAllowList?: string[];
|
|
342
340
|
destinationBlockList?: string[];
|
|
@@ -349,7 +347,7 @@ export class VpnManager {
|
|
|
349
347
|
const client = this.clients.get(clientId);
|
|
350
348
|
if (!client) throw new Error(`Client not found: ${clientId}`);
|
|
351
349
|
if (update.description !== undefined) client.description = update.description;
|
|
352
|
-
if (update.
|
|
350
|
+
if (update.targetProfileIds !== undefined) client.targetProfileIds = update.targetProfileIds;
|
|
353
351
|
if (update.forceDestinationSmartproxy !== undefined) client.forceDestinationSmartproxy = update.forceDestinationSmartproxy;
|
|
354
352
|
if (update.destinationAllowList !== undefined) client.destinationAllowList = update.destinationAllowList;
|
|
355
353
|
if (update.destinationBlockList !== undefined) client.destinationBlockList = update.destinationBlockList;
|
|
@@ -409,10 +407,10 @@ export class VpnManager {
|
|
|
409
407
|
);
|
|
410
408
|
}
|
|
411
409
|
|
|
412
|
-
// Override AllowedIPs with per-client values based on
|
|
410
|
+
// Override AllowedIPs with per-client values based on target profiles
|
|
413
411
|
if (this.config.getClientAllowedIPs) {
|
|
414
|
-
const
|
|
415
|
-
const allowedIPs = await this.config.getClientAllowedIPs(
|
|
412
|
+
const profileIds = persisted?.targetProfileIds || [];
|
|
413
|
+
const allowedIPs = await this.config.getClientAllowedIPs(profileIds);
|
|
416
414
|
config = config.replace(
|
|
417
415
|
/AllowedIPs\s*=\s*.+/,
|
|
418
416
|
`AllowedIPs = ${allowedIPs.join(', ')}`,
|
|
@@ -423,22 +421,6 @@ export class VpnManager {
|
|
|
423
421
|
return config;
|
|
424
422
|
}
|
|
425
423
|
|
|
426
|
-
// ── Tag-based access control ───────────────────────────────────────────
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Get assigned IPs for all enabled clients matching any of the given server-defined tags.
|
|
430
|
-
*/
|
|
431
|
-
public getClientIpsForServerDefinedTags(tags: string[]): string[] {
|
|
432
|
-
const ips: string[] = [];
|
|
433
|
-
for (const client of this.clients.values()) {
|
|
434
|
-
if (!client.enabled || !client.assignedIp) continue;
|
|
435
|
-
if (client.serverDefinedClientTags?.some(t => tags.includes(t))) {
|
|
436
|
-
ips.push(client.assignedIp);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
return ips;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
424
|
// ── Status and telemetry ───────────────────────────────────────────────
|
|
443
425
|
|
|
444
426
|
/**
|
|
@@ -548,12 +530,6 @@ export class VpnManager {
|
|
|
548
530
|
private async loadPersistedClients(): Promise<void> {
|
|
549
531
|
const docs = await VpnClientDoc.findAll();
|
|
550
532
|
for (const doc of docs) {
|
|
551
|
-
// Migrate legacy `tags` → `serverDefinedClientTags`
|
|
552
|
-
if (!doc.serverDefinedClientTags && (doc as any).tags) {
|
|
553
|
-
doc.serverDefinedClientTags = (doc as any).tags;
|
|
554
|
-
(doc as any).tags = undefined;
|
|
555
|
-
await doc.save();
|
|
556
|
-
}
|
|
557
533
|
this.clients.set(doc.clientId, doc);
|
|
558
534
|
}
|
|
559
535
|
if (this.clients.size > 0) {
|