node-opcua-server-configuration 2.70.2 → 2.72.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/clientTools/push_certificate_management_client.js +7 -9
- package/dist/clientTools/push_certificate_management_client.js.map +1 -1
- package/dist/server/install_CertificateAlarm.d.ts +8 -2
- package/dist/server/install_CertificateAlarm.js +20 -12
- package/dist/server/install_CertificateAlarm.js.map +1 -1
- package/dist/server/install_certificate_file_watcher.d.ts +5 -0
- package/dist/server/install_certificate_file_watcher.js +24 -0
- package/dist/server/install_certificate_file_watcher.js.map +1 -0
- package/dist/server/install_push_certitifate_management.d.ts +9 -5
- package/dist/server/install_push_certitifate_management.js +1 -2
- package/dist/server/install_push_certitifate_management.js.map +1 -1
- package/dist/server/push_certificate_manager_helpers.d.ts +2 -5
- package/dist/server/push_certificate_manager_helpers.js +119 -16
- package/dist/server/push_certificate_manager_helpers.js.map +1 -1
- package/dist/server/push_certificate_manager_server_impl.d.ts +1 -1
- package/dist/server/push_certificate_manager_server_impl.js +3 -3
- package/dist/server/push_certificate_manager_server_impl.js.map +1 -1
- package/dist/server/tools.js.map +1 -1
- package/dist/server/trust_list_server.js +1 -1
- package/dist/server/trust_list_server.js.map +1 -1
- package/package.json +26 -24
- package/source/clientTools/push_certificate_management_client.ts +16 -37
- package/source/server/install_CertificateAlarm.ts +35 -29
- package/source/server/install_certificate_file_watcher.ts +25 -0
- package/source/server/install_push_certitifate_management.ts +8 -8
- package/source/server/push_certificate_manager_helpers.ts +142 -26
- package/source/server/push_certificate_manager_server_impl.ts +6 -7
- package/source/server/tools.ts +1 -1
- package/source/server/trust_list_server.ts +3 -3
|
@@ -1,35 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opcua-server-configuration
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import * as fs from "fs";
|
|
7
6
|
import {
|
|
8
7
|
AddressSpace,
|
|
9
8
|
SessionContext,
|
|
10
9
|
UAMethod,
|
|
11
10
|
UATrustList,
|
|
12
|
-
UAObject,
|
|
13
|
-
UAVariable,
|
|
14
11
|
UAServerConfiguration,
|
|
15
|
-
ISessionContext
|
|
12
|
+
ISessionContext,
|
|
13
|
+
UACertificateGroup,
|
|
14
|
+
UACertificateExpirationAlarmEx,
|
|
15
|
+
UACertificateExpirationAlarmImpl
|
|
16
16
|
} from "node-opcua-address-space";
|
|
17
|
+
import { UAObject, UAVariable, EventNotifierFlags } from "node-opcua-address-space-base";
|
|
18
|
+
|
|
17
19
|
import { checkDebugFlag, make_debugLog, make_warningLog } from "node-opcua-debug";
|
|
18
20
|
import { NodeId, resolveNodeId } from "node-opcua-nodeid";
|
|
19
21
|
import { StatusCodes } from "node-opcua-status-code";
|
|
20
22
|
import { CallMethodResultOptions } from "node-opcua-types";
|
|
21
23
|
import { DataType, Variant, VariantArrayType } from "node-opcua-variant";
|
|
22
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
AccessLevelFlag,
|
|
26
|
+
AccessRestrictionsFlag,
|
|
27
|
+
BrowseDirection,
|
|
28
|
+
coerceQualifiedName,
|
|
29
|
+
NodeClass,
|
|
30
|
+
QualifiedName
|
|
31
|
+
} from "node-opcua-data-model";
|
|
23
32
|
import { ByteString, UAString } from "node-opcua-basic-types";
|
|
24
|
-
import { ObjectTypeIds } from "node-opcua-constants";
|
|
33
|
+
import { ObjectIds, ObjectTypeIds } from "node-opcua-constants";
|
|
34
|
+
import { CertificateManager } from "node-opcua-certificate-manager";
|
|
35
|
+
import { Certificate, readCertificate } from "node-opcua-crypto";
|
|
25
36
|
|
|
26
37
|
import { CreateSigningRequestResult, PushCertificateManager } from "../push_certificate_manager";
|
|
27
38
|
|
|
28
|
-
import { installCertificateExpirationAlarm } from "./install_CertificateAlarm";
|
|
29
39
|
import { PushCertificateManagerServerImpl, PushCertificateManagerServerOptions } from "./push_certificate_manager_server_impl";
|
|
30
40
|
import { installAccessRestrictionOnTrustList, promoteTrustList } from "./promote_trust_list";
|
|
31
41
|
import { hasEncryptedChannel, hasExpectedUserAccess } from "./tools";
|
|
32
42
|
import { rolePermissionAdminOnly, rolePermissionRestricted } from "./roles_and_permissions";
|
|
43
|
+
import { installCertificateFileWatcher } from "./install_certificate_file_watcher";
|
|
33
44
|
|
|
34
45
|
const debugLog = make_debugLog("ServerConfiguration");
|
|
35
46
|
const doDebug = checkDebugFlag("ServerConfiguration");
|
|
@@ -247,38 +258,136 @@ async function _applyChanges(
|
|
|
247
258
|
return { statusCode };
|
|
248
259
|
}
|
|
249
260
|
|
|
261
|
+
function getCertificateFilename(certificateManager: CertificateManager): string {
|
|
262
|
+
return path.join(certificateManager.rootDir, "own/certs/certificate.pem"); // to do , find a better way
|
|
263
|
+
}
|
|
264
|
+
async function getCertificate(certificateManager: CertificateManager): Promise<Certificate | null> {
|
|
265
|
+
try {
|
|
266
|
+
const certificateFile = getCertificateFilename(certificateManager);
|
|
267
|
+
if (fs.existsSync(certificateFile)) {
|
|
268
|
+
const certificate = await readCertificate(certificateFile);
|
|
269
|
+
return certificate;
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
} catch (err) {
|
|
273
|
+
warningLog("getCertificate Error", (err as Error).message);
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function bindCertificateGroup(certificateGroup: UACertificateGroup, certificateManager?: CertificateManager) {
|
|
279
|
+
if (certificateManager) {
|
|
280
|
+
const certificateFile = getCertificateFilename(certificateManager);
|
|
281
|
+
const changeDetector = installCertificateFileWatcher(certificateGroup, certificateFile);
|
|
282
|
+
changeDetector.on("certificateChange", () => {
|
|
283
|
+
debugLog("detecting certificate change", certificateFile);
|
|
284
|
+
updateCertificateAlarm();
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async function updateCertificateAlarm() {
|
|
289
|
+
try {
|
|
290
|
+
debugLog("updateCertificateAlarm", certificateGroup.browseName.toString());
|
|
291
|
+
const certificateExpired = certificateGroup.getComponentByName("CertificateExpired");
|
|
292
|
+
if (certificateExpired && certificateManager) {
|
|
293
|
+
const certificateExpiredEx = certificateExpired as unknown as UACertificateExpirationAlarmEx;
|
|
294
|
+
const certificate = await getCertificate(certificateManager);
|
|
295
|
+
certificateExpiredEx.setCertificate(certificate);
|
|
296
|
+
}
|
|
297
|
+
} catch (err) {
|
|
298
|
+
warningLog("updateCertificateAlarm Error", (err as Error).message);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const addressSpace = certificateGroup.addressSpace;
|
|
303
|
+
if (!certificateManager) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const trustList = certificateGroup.getComponentByName("TrustList");
|
|
307
|
+
if (trustList) {
|
|
308
|
+
(trustList as any).$$certificateManager = certificateManager;
|
|
309
|
+
}
|
|
310
|
+
const certificateExpired = certificateGroup.getComponentByName("CertificateExpired");
|
|
311
|
+
if (certificateExpired) {
|
|
312
|
+
(certificateExpired as any).$$certificateManager = certificateManager;
|
|
313
|
+
// install alarm handling
|
|
314
|
+
const timerId = setInterval(updateCertificateAlarm, 60 * 1000);
|
|
315
|
+
addressSpace.registerShutdownTask(() => clearInterval(timerId));
|
|
316
|
+
updateCertificateAlarm();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
250
320
|
function bindCertificateManager(addressSpace: AddressSpace, options: PushCertificateManagerServerOptions) {
|
|
251
321
|
const serverConfiguration = addressSpace.rootFolder.objects.server.getChildByName(
|
|
252
322
|
"ServerConfiguration"
|
|
253
323
|
)! as UAServerConfiguration;
|
|
254
324
|
|
|
255
|
-
const defaultApplicationGroup = serverConfiguration.certificateGroups.getComponentByName(
|
|
325
|
+
const defaultApplicationGroup = serverConfiguration.certificateGroups.getComponentByName(
|
|
326
|
+
"DefaultApplicationGroup"
|
|
327
|
+
) as UACertificateGroup | null;
|
|
256
328
|
if (defaultApplicationGroup) {
|
|
257
|
-
|
|
258
|
-
if (trustList) {
|
|
259
|
-
(trustList as any).$$certificateManager = options.applicationGroup;
|
|
260
|
-
}
|
|
329
|
+
bindCertificateGroup(defaultApplicationGroup, options.applicationGroup);
|
|
261
330
|
}
|
|
262
|
-
const defaultTokenGroup = serverConfiguration.certificateGroups.getComponentByName(
|
|
331
|
+
const defaultTokenGroup = serverConfiguration.certificateGroups.getComponentByName(
|
|
332
|
+
"DefaultUserTokenGroup"
|
|
333
|
+
) as UACertificateGroup | null;
|
|
263
334
|
if (defaultTokenGroup) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
335
|
+
bindCertificateGroup(defaultTokenGroup, options.userTokenGroup);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function setNotifierOfChain(childObject: UAObject | null) {
|
|
340
|
+
if (!childObject) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const parentObject: UAObject | null = childObject.parent as UAObject | null;
|
|
344
|
+
if (!parentObject) {
|
|
345
|
+
return;
|
|
268
346
|
}
|
|
347
|
+
const notifierOf = childObject.findReferencesEx("HasNotifier", BrowseDirection.Inverse);
|
|
348
|
+
if (notifierOf.length === 0) {
|
|
349
|
+
const notifierOfNode = childObject.addReference({
|
|
350
|
+
referenceType: "HasNotifier",
|
|
351
|
+
nodeId: parentObject.nodeId,
|
|
352
|
+
isForward: false
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
parentObject.setEventNotifier(parentObject.eventNotifier | EventNotifierFlags.SubscribeToEvents);
|
|
356
|
+
if (parentObject.nodeId.namespace === 0 && parentObject.nodeId.value === ObjectIds.Server) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
setNotifierOfChain(parentObject);
|
|
269
360
|
}
|
|
270
361
|
|
|
271
|
-
export async function promoteCertificateGroup(certificateGroup:
|
|
362
|
+
export async function promoteCertificateGroup(certificateGroup: UACertificateGroup): Promise<void> {
|
|
272
363
|
const trustList = certificateGroup.getChildByName("TrustList") as UATrustList;
|
|
273
364
|
if (trustList) {
|
|
274
|
-
promoteTrustList(trustList);
|
|
275
|
-
}
|
|
365
|
+
await promoteTrustList(trustList);
|
|
366
|
+
}
|
|
367
|
+
if (!certificateGroup.certificateExpired) {
|
|
368
|
+
const namespace = certificateGroup.addressSpace.getOwnNamespace();
|
|
369
|
+
|
|
370
|
+
// certificateGroup.
|
|
371
|
+
UACertificateExpirationAlarmImpl.instantiate(namespace, "CertificateExpirationAlarmType", {
|
|
372
|
+
browseName: coerceQualifiedName("0:CertificateExpired"),
|
|
373
|
+
componentOf: certificateGroup,
|
|
374
|
+
conditionSource: null,
|
|
375
|
+
conditionOf: certificateGroup,
|
|
376
|
+
inputNode: NodeId.nullNodeId,
|
|
377
|
+
normalState: NodeId.nullNodeId,
|
|
378
|
+
optionals: ["ExpirationLimit"]
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
certificateGroup.setEventNotifier(EventNotifierFlags.SubscribeToEvents);
|
|
382
|
+
setNotifierOfChain(certificateGroup);
|
|
276
383
|
}
|
|
277
384
|
|
|
278
385
|
export async function installPushCertificateManagement(
|
|
279
386
|
addressSpace: AddressSpace,
|
|
280
387
|
options: PushCertificateManagerServerOptions
|
|
281
388
|
): Promise<void> {
|
|
389
|
+
addressSpace.installAlarmsAndConditionsService();
|
|
390
|
+
|
|
282
391
|
const serverConfiguration = addressSpace.rootFolder.objects.server.getChildByName(
|
|
283
392
|
"ServerConfiguration"
|
|
284
393
|
)! as UAServerConfiguration;
|
|
@@ -322,8 +431,8 @@ export async function installPushCertificateManagement(
|
|
|
322
431
|
}
|
|
323
432
|
}
|
|
324
433
|
for (const group of certificateGroups.getComponents()) {
|
|
325
|
-
group
|
|
326
|
-
group
|
|
434
|
+
group.setRolePermissions(rolePermissionAdminOnly);
|
|
435
|
+
group.setAccessRestrictions(AccessRestrictionsFlag.SigningRequired | AccessRestrictionsFlag.EncryptionRequired);
|
|
327
436
|
if (group.nodeClass === NodeClass.Object) {
|
|
328
437
|
installAccessRestrictionOnGroup(group as UAObject);
|
|
329
438
|
}
|
|
@@ -359,7 +468,7 @@ export async function installPushCertificateManagement(
|
|
|
359
468
|
serverConfiguration.applyChanges!.bindMethod(_applyChanges);
|
|
360
469
|
}
|
|
361
470
|
|
|
362
|
-
installCertificateExpirationAlarm(addressSpace);
|
|
471
|
+
//xx installCertificateExpirationAlarm(addressSpace);
|
|
363
472
|
|
|
364
473
|
const cg = serverConfiguration.certificateGroups.getComponents();
|
|
365
474
|
|
|
@@ -370,11 +479,18 @@ export async function installPushCertificateManagement(
|
|
|
370
479
|
arrayType: VariantArrayType.Array,
|
|
371
480
|
value: [resolveNodeId(ObjectTypeIds.RsaSha256ApplicationCertificateType)]
|
|
372
481
|
});
|
|
482
|
+
|
|
483
|
+
const certificateGroupType = addressSpace.findObjectType("CertificateGroupType")!;
|
|
484
|
+
|
|
373
485
|
for (const certificateGroup of cg) {
|
|
374
486
|
if (certificateGroup.nodeClass !== NodeClass.Object) {
|
|
375
487
|
continue;
|
|
376
488
|
}
|
|
377
|
-
|
|
489
|
+
const o = certificateGroup as UAObject;
|
|
490
|
+
if (!o.typeDefinitionObj.isSupertypeOf(certificateGroupType)) {
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
await promoteCertificateGroup(certificateGroup as UACertificateGroup);
|
|
378
494
|
}
|
|
379
495
|
await bindCertificateManager(addressSpace, options);
|
|
380
496
|
}
|
|
@@ -6,11 +6,7 @@ import * as fs from "fs";
|
|
|
6
6
|
import * as path from "path";
|
|
7
7
|
import { promisify} from "util";
|
|
8
8
|
import * as rimraf from "rimraf";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// node 14 onward : import { readFile, writeFile, readdir } from "fs/promises";
|
|
12
|
-
const { readFile, writeFile, readdir } = fs.promises;
|
|
13
|
-
|
|
9
|
+
import { SubjectOptions } from "node-opcua-pki";
|
|
14
10
|
import { assert } from "node-opcua-assert";
|
|
15
11
|
import { ByteString, StatusCodes } from "node-opcua-basic-types";
|
|
16
12
|
import {
|
|
@@ -41,7 +37,10 @@ import {
|
|
|
41
37
|
PushCertificateManager,
|
|
42
38
|
UpdateCertificateResult
|
|
43
39
|
} from "../push_certificate_manager";
|
|
44
|
-
|
|
40
|
+
|
|
41
|
+
// node 14 onward : import { readFile, writeFile, readdir } from "fs/promises";
|
|
42
|
+
const { readFile, writeFile, readdir } = fs.promises;
|
|
43
|
+
|
|
45
44
|
|
|
46
45
|
const debugLog = make_debugLog("ServerConfiguration");
|
|
47
46
|
const errorLog = make_errorLog("ServerConfiguration");
|
|
@@ -284,7 +283,7 @@ export class PushCertificateManagerServerImpl extends EventEmitter implements Pu
|
|
|
284
283
|
await fs.promises.mkdir(location);
|
|
285
284
|
}
|
|
286
285
|
|
|
287
|
-
|
|
286
|
+
const destCertificateManager = certificateManager;
|
|
288
287
|
const keySize = (certificateManager as any).keySize; // because keySize is private !
|
|
289
288
|
certificateManager = new CertificateManager({
|
|
290
289
|
keySize,
|
package/source/server/tools.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ISessionContext,
|
|
1
|
+
import { ISessionContext, WellKnownRoles } from "node-opcua-address-space";
|
|
2
2
|
import { MessageSecurityMode } from "node-opcua-secure-channel";
|
|
3
3
|
|
|
4
4
|
export function hasExpectedUserAccess(context: ISessionContext) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { OPCUACertificateManager } from "node-opcua-certificate-manager";
|
|
2
|
-
import { TrustListDataType } from "node-opcua-types";
|
|
3
|
-
|
|
4
1
|
import *as fs from "fs";
|
|
5
2
|
import * as path from "path";
|
|
3
|
+
|
|
4
|
+
import { OPCUACertificateManager } from "node-opcua-certificate-manager";
|
|
5
|
+
import { TrustListDataType } from "node-opcua-types";
|
|
6
6
|
import { AbstractFs } from "node-opcua-file-transfer";
|
|
7
7
|
import { BinaryStream } from "node-opcua-binary-stream";
|
|
8
8
|
import { readCertificate, readCertificateRevocationList } from "node-opcua-crypto";
|