node-opcua-server-configuration 2.163.1 → 2.165.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/certificate_types.d.ts +15 -0
- package/dist/clientTools/certificate_types.js +19 -0
- package/dist/clientTools/certificate_types.js.map +1 -0
- package/dist/clientTools/get_certificate_key_type.d.ts +6 -0
- package/dist/clientTools/get_certificate_key_type.js +55 -0
- package/dist/clientTools/get_certificate_key_type.js.map +1 -0
- package/dist/clientTools/index.d.ts +2 -1
- package/dist/clientTools/index.js +2 -17
- package/dist/clientTools/index.js.map +1 -1
- package/dist/clientTools/push_certificate_management_client.d.ts +10 -10
- package/dist/clientTools/push_certificate_management_client.js +85 -89
- package/dist/clientTools/push_certificate_management_client.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.js +9 -23
- package/dist/index.js.map +1 -1
- package/dist/push_certificate_manager.d.ts +6 -5
- package/dist/push_certificate_manager.js +1 -2
- package/dist/server/certificate_validation.d.ts +15 -0
- package/dist/server/certificate_validation.js +76 -0
- package/dist/server/certificate_validation.js.map +1 -0
- package/dist/server/file_transaction_manager.d.ts +30 -0
- package/dist/server/file_transaction_manager.js +223 -0
- package/dist/server/file_transaction_manager.js.map +1 -0
- package/dist/server/install_certificate_file_watcher.d.ts +1 -1
- package/dist/server/install_certificate_file_watcher.js +8 -14
- package/dist/server/install_certificate_file_watcher.js.map +1 -1
- package/dist/server/install_push_certitifate_management.d.ts +6 -6
- package/dist/server/install_push_certitifate_management.js +59 -81
- package/dist/server/install_push_certitifate_management.js.map +1 -1
- package/dist/server/promote_trust_list.d.ts +1 -1
- package/dist/server/promote_trust_list.js +348 -82
- package/dist/server/promote_trust_list.js.map +1 -1
- package/dist/server/push_certificate_manager/apply_changes.d.ts +4 -0
- package/dist/server/push_certificate_manager/apply_changes.js +65 -0
- package/dist/server/push_certificate_manager/apply_changes.js.map +1 -0
- package/dist/server/push_certificate_manager/create_signing_request.d.ts +5 -0
- package/dist/server/push_certificate_manager/create_signing_request.js +108 -0
- package/dist/server/push_certificate_manager/create_signing_request.js.map +1 -0
- package/dist/server/push_certificate_manager/get_rejected_list.d.ts +3 -0
- package/dist/server/push_certificate_manager/get_rejected_list.js +46 -0
- package/dist/server/push_certificate_manager/get_rejected_list.js.map +1 -0
- package/dist/server/push_certificate_manager/internal_context.d.ts +35 -0
- package/dist/server/push_certificate_manager/internal_context.js +45 -0
- package/dist/server/push_certificate_manager/internal_context.js.map +1 -0
- package/dist/server/push_certificate_manager/subject_to_string.d.ts +3 -0
- package/dist/server/push_certificate_manager/subject_to_string.js +27 -0
- package/dist/server/push_certificate_manager/subject_to_string.js.map +1 -0
- package/dist/server/push_certificate_manager/update_certificate.d.ts +5 -0
- package/dist/server/push_certificate_manager/update_certificate.js +134 -0
- package/dist/server/push_certificate_manager/update_certificate.js.map +1 -0
- package/dist/server/push_certificate_manager/util.d.ts +29 -0
- package/dist/server/push_certificate_manager/util.js +117 -0
- package/dist/server/push_certificate_manager/util.js.map +1 -0
- package/dist/server/push_certificate_manager_helpers.d.ts +5 -2
- package/dist/server/push_certificate_manager_helpers.js +110 -113
- package/dist/server/push_certificate_manager_helpers.js.map +1 -1
- package/dist/server/push_certificate_manager_server_impl.d.ts +37 -30
- package/dist/server/push_certificate_manager_server_impl.js +58 -438
- package/dist/server/push_certificate_manager_server_impl.js.map +1 -1
- package/dist/server/roles_and_permissions.d.ts +1 -1
- package/dist/server/roles_and_permissions.js +24 -27
- package/dist/server/roles_and_permissions.js.map +1 -1
- package/dist/server/tools.d.ts +1 -1
- package/dist/server/tools.js +7 -13
- package/dist/server/tools.js.map +1 -1
- package/dist/server/trust_list_server.d.ts +2 -2
- package/dist/server/trust_list_server.js +40 -29
- package/dist/server/trust_list_server.js.map +1 -1
- package/dist/standard_certificate_types.js +6 -9
- package/dist/standard_certificate_types.js.map +1 -1
- package/dist/trust_list.d.ts +2 -2
- package/dist/trust_list.js +1 -2
- package/dist/trust_list_impl.js +1 -2
- package/dist/trust_list_impl.js.map +1 -1
- package/package.json +30 -30
- package/source/clientTools/certificate_types.ts +21 -0
- package/source/clientTools/get_certificate_key_type.ts +73 -0
- package/source/clientTools/index.ts +2 -1
- package/source/clientTools/push_certificate_management_client.ts +49 -44
- package/source/index.ts +9 -7
- package/source/push_certificate_manager.ts +17 -18
- package/source/server/certificate_validation.ts +103 -0
- package/source/server/file_transaction_manager.ts +253 -0
- package/source/server/install_certificate_file_watcher.ts +15 -11
- package/source/server/install_push_certitifate_management.ts +52 -68
- package/source/server/promote_trust_list.ts +392 -73
- package/source/server/push_certificate_manager/apply_changes.ts +73 -0
- package/source/server/push_certificate_manager/create_signing_request.ts +137 -0
- package/source/server/push_certificate_manager/get_rejected_list.ts +63 -0
- package/source/server/push_certificate_manager/internal_context.ts +63 -0
- package/source/server/push_certificate_manager/subject_to_string.ts +25 -0
- package/source/server/push_certificate_manager/update_certificate.ts +203 -0
- package/source/server/push_certificate_manager/util.ts +145 -0
- package/source/server/push_certificate_manager_helpers.ts +62 -52
- package/source/server/push_certificate_manager_server_impl.ts +133 -552
- package/source/server/roles_and_permissions.ts +7 -8
- package/source/server/tools.ts +2 -5
- package/source/server/trust_list_server.ts +24 -9
- package/source/standard_certificate_types.ts +2 -3
- package/source/trust_list.ts +26 -33
|
@@ -1,77 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opcua-server-configuration-server
|
|
3
3
|
*/
|
|
4
|
-
import { EventEmitter } from "events";
|
|
5
|
-
import fs from "fs";
|
|
6
|
-
import path from "path";
|
|
7
|
-
import { rimraf } from "rimraf";
|
|
8
|
-
import { SubjectOptions } from "node-opcua-pki";
|
|
4
|
+
import { EventEmitter } from "node:events";
|
|
9
5
|
import { assert } from "node-opcua-assert";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
convertPEMtoDER,
|
|
13
|
-
exploreCertificate,
|
|
14
|
-
makeSHA1Thumbprint,
|
|
15
|
-
toPem,
|
|
16
|
-
PrivateKey,
|
|
17
|
-
certificateMatchesPrivateKey,
|
|
18
|
-
coercePEMorDerToPrivateKey,
|
|
19
|
-
coercePrivateKeyPem,
|
|
20
|
-
} from "node-opcua-crypto/web";
|
|
21
|
-
|
|
22
|
-
import { readPrivateKey, readCertificate } from "node-opcua-crypto";
|
|
23
|
-
|
|
24
|
-
// fixme: use the one from node-opcua-crypto
|
|
25
|
-
export interface DirectoryName {
|
|
26
|
-
stateOrProvinceName?: string;
|
|
27
|
-
localityName?: string;
|
|
28
|
-
organizationName?: string;
|
|
29
|
-
organizationUnitName?: string;
|
|
30
|
-
commonName?: string;
|
|
31
|
-
countryName?: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
|
|
35
|
-
import { NodeId, resolveNodeId, sameNodeId } from "node-opcua-nodeid";
|
|
6
|
+
import type { ISessionContext } from "node-opcua-address-space-base";
|
|
7
|
+
import type { ByteString } from "node-opcua-basic-types";
|
|
36
8
|
import { CertificateManager } from "node-opcua-certificate-manager";
|
|
37
|
-
import {
|
|
38
|
-
|
|
39
|
-
import {
|
|
9
|
+
import { make_errorLog } from "node-opcua-debug";
|
|
10
|
+
import type { NodeId } from "node-opcua-nodeid";
|
|
11
|
+
import type { SubjectOptions } from "node-opcua-pki";
|
|
12
|
+
import type { StatusCode } from "node-opcua-status-code";
|
|
13
|
+
import { rsaCertificateTypesArray } from "../clientTools/certificate_types.js";
|
|
14
|
+
import type {
|
|
40
15
|
CreateSigningRequestResult,
|
|
41
16
|
GetRejectedListResult,
|
|
42
17
|
PushCertificateManager,
|
|
43
18
|
UpdateCertificateResult
|
|
44
|
-
} from "../push_certificate_manager";
|
|
19
|
+
} from "../push_certificate_manager.js";
|
|
20
|
+
import { executeApplyChanges } from "./push_certificate_manager/apply_changes.js";
|
|
21
|
+
import { executeCreateSigningRequest } from "./push_certificate_manager/create_signing_request.js";
|
|
22
|
+
import { executeGetRejectedList } from "./push_certificate_manager/get_rejected_list.js";
|
|
23
|
+
import { PushCertificateManagerInternalContext } from "./push_certificate_manager/internal_context.js";
|
|
24
|
+
import { executeUpdateCertificate } from "./push_certificate_manager/update_certificate.js";
|
|
45
25
|
|
|
46
|
-
// node 14 onward : import { readFile, writeFile, readdir } from "fs/promises";
|
|
47
|
-
const { readFile, writeFile, readdir } = fs.promises;
|
|
48
|
-
|
|
49
|
-
const debugLog = make_debugLog("ServerConfiguration");
|
|
50
26
|
const errorLog = make_errorLog("ServerConfiguration");
|
|
51
|
-
const warningLog = make_warningLog("ServerConfiguration");
|
|
52
|
-
const doDebug = checkDebugFlag("ServerConfiguration");
|
|
53
|
-
doDebug;
|
|
54
|
-
|
|
55
|
-
const defaultApplicationGroup = resolveNodeId("ServerConfiguration_CertificateGroups_DefaultApplicationGroup");
|
|
56
|
-
const defaultHttpsGroup = resolveNodeId("ServerConfiguration_CertificateGroups_DefaultHttpsGroup");
|
|
57
|
-
const defaultUserTokenGroup = resolveNodeId("ServerConfiguration_CertificateGroups_DefaultUserTokenGroup");
|
|
58
|
-
|
|
59
|
-
|
|
60
27
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Type-safe event map for PushCertificateManagerServerImpl.
|
|
30
|
+
*
|
|
31
|
+
* - `applyChangesCompleted` — fired after a successful `applyChanges()` call.
|
|
32
|
+
* - `certificateUpdated` — fired after a successful `updateCertificate()` call,
|
|
33
|
+
* with the resolved group id and the leaf certificate.
|
|
34
|
+
* - `trustListUpdated` — fired after a successful TrustList mutation
|
|
35
|
+
* (`CloseAndUpdate`, `AddCertificate`, `RemoveCertificate`),
|
|
36
|
+
* with the certificate-group browse-name.
|
|
37
|
+
*/
|
|
38
|
+
export interface PushCertificateManagerEvents {
|
|
39
|
+
applyChangesCompleted: (context?: ISessionContext) => void;
|
|
40
|
+
certificateUpdated: (certificateGroupId: NodeId | string, certificate: Buffer) => void;
|
|
41
|
+
trustListUpdated: (certificateGroupId: string) => void;
|
|
75
42
|
}
|
|
76
43
|
|
|
77
44
|
export interface PushCertificateManagerServerOptions {
|
|
@@ -80,76 +47,15 @@ export interface PushCertificateManagerServerOptions {
|
|
|
80
47
|
httpsGroup?: CertificateManager;
|
|
81
48
|
|
|
82
49
|
applicationUri: string;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
type Functor = () => Promise<void>;
|
|
86
50
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
} catch (err) {
|
|
95
|
-
errorLog(err);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export async function deleteFile(file: string): Promise<void> {
|
|
100
|
-
try {
|
|
101
|
-
const exists = await fs.existsSync(file);
|
|
102
|
-
if (exists) {
|
|
103
|
-
debugLog("deleting file ", file);
|
|
104
|
-
await fs.promises.unlink(file);
|
|
105
|
-
}
|
|
106
|
-
} catch (err) {
|
|
107
|
-
errorLog(err);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export async function moveFile(source: string, dest: string): Promise<void> {
|
|
112
|
-
debugLog("moving file file \n source ", source, "\n =>\n dest ", dest);
|
|
113
|
-
try {
|
|
114
|
-
await copyFile(source, dest);
|
|
115
|
-
await deleteFile(source);
|
|
116
|
-
} catch (err) {
|
|
117
|
-
errorLog(err);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export async function moveFileWithBackup(source: string, dest: string): Promise<void> {
|
|
122
|
-
// let make a copy of the destination file
|
|
123
|
-
debugLog("moveFileWithBackup file \n source ", source, "\n =>\n dest ", dest);
|
|
124
|
-
await copyFile(dest, dest + "_old");
|
|
125
|
-
await moveFile(source, dest);
|
|
51
|
+
// Optional: Allowed certificate types for each group
|
|
52
|
+
// These should be read from the CertificateTypes Property of the CertificateGroup objects in the AddressSpace
|
|
53
|
+
// If not provided, defaults to all known OPC UA certificate types (backward compatibility)
|
|
54
|
+
applicationGroupCertificateTypes?: NodeId[];
|
|
55
|
+
userTokenGroupCertificateTypes?: NodeId[];
|
|
56
|
+
httpsGroupCertificateTypes?: NodeId[];
|
|
126
57
|
}
|
|
127
58
|
|
|
128
|
-
|
|
129
|
-
export function subjectToString(subject: SubjectOptions & DirectoryName): string {
|
|
130
|
-
let s = "";
|
|
131
|
-
subject.commonName && (s += `/CN=${subject.commonName}`);
|
|
132
|
-
|
|
133
|
-
subject.country && (s += `/C=${subject.country}`);
|
|
134
|
-
subject.countryName && (s += `/C=${subject.countryName}`);
|
|
135
|
-
|
|
136
|
-
subject.domainComponent && (s += `/DC=${subject.domainComponent}`);
|
|
137
|
-
|
|
138
|
-
subject.locality && (s += `/L=${subject.locality}`);
|
|
139
|
-
subject.localityName && (s += `/L=${subject.localityName}`);
|
|
140
|
-
|
|
141
|
-
subject.organization && (s += `/O=${subject.organization}`);
|
|
142
|
-
subject.organizationName && (s += `/O=${subject.organizationName}`);
|
|
143
|
-
|
|
144
|
-
subject.organizationUnitName && (s += `/OU=${subject.organizationUnitName}`);
|
|
145
|
-
|
|
146
|
-
subject.state && (s += `/ST=${subject.state}`);
|
|
147
|
-
subject.stateOrProvinceName && (s += `/ST=${subject.stateOrProvinceName}`);
|
|
148
|
-
|
|
149
|
-
return s;
|
|
150
|
-
}
|
|
151
|
-
let fileCounter = 0;
|
|
152
|
-
|
|
153
59
|
export type ActionQueue = (() => Promise<void>)[];
|
|
154
60
|
|
|
155
61
|
export class PushCertificateManagerServerImpl extends EventEmitter implements PushCertificateManager {
|
|
@@ -157,15 +63,38 @@ export class PushCertificateManagerServerImpl extends EventEmitter implements Pu
|
|
|
157
63
|
public userTokenGroup?: CertificateManager;
|
|
158
64
|
public httpsGroup?: CertificateManager;
|
|
159
65
|
|
|
160
|
-
private
|
|
161
|
-
private readonly
|
|
162
|
-
|
|
163
|
-
|
|
66
|
+
// Use a true private reference (could be upgraded to #context in recent ES)
|
|
67
|
+
private readonly _context: PushCertificateManagerInternalContext;
|
|
68
|
+
|
|
69
|
+
/** @hidden */
|
|
70
|
+
public applicationUri: string;
|
|
71
|
+
|
|
72
|
+
// ── typed event helpers ──────────────────────────────────────────
|
|
73
|
+
public on<K extends keyof PushCertificateManagerEvents>(
|
|
74
|
+
event: K,
|
|
75
|
+
listener: PushCertificateManagerEvents[K]
|
|
76
|
+
): this;
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
+
public on(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
+
public on(event: string | symbol, listener: (...args: any[]) => void): this {
|
|
81
|
+
return super.on(event, listener);
|
|
82
|
+
}
|
|
164
83
|
|
|
165
|
-
|
|
84
|
+
public once<K extends keyof PushCertificateManagerEvents>(
|
|
85
|
+
event: K,
|
|
86
|
+
listener: PushCertificateManagerEvents[K]
|
|
87
|
+
): this;
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
89
|
+
public once(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
public once(event: string | symbol, listener: (...args: any[]) => void): this {
|
|
92
|
+
return super.once(event, listener);
|
|
93
|
+
}
|
|
166
94
|
|
|
167
95
|
constructor(options: PushCertificateManagerServerOptions) {
|
|
168
96
|
super();
|
|
97
|
+
this._context = new PushCertificateManagerInternalContext(this);
|
|
169
98
|
|
|
170
99
|
this.applicationUri = options ? options.applicationUri : "";
|
|
171
100
|
|
|
@@ -174,23 +103,38 @@ export class PushCertificateManagerServerImpl extends EventEmitter implements Pu
|
|
|
174
103
|
this.userTokenGroup = options.userTokenGroup;
|
|
175
104
|
this.httpsGroup = options.httpsGroup;
|
|
176
105
|
if (this.userTokenGroup) {
|
|
177
|
-
this.
|
|
178
|
-
|
|
179
|
-
|
|
106
|
+
this._context.map.DefaultUserTokenGroup = this.userTokenGroup;
|
|
107
|
+
// Store allowed certificate types, or use all known types as default
|
|
108
|
+
this._context.certificateTypes.DefaultUserTokenGroup = options.userTokenGroupCertificateTypes || [
|
|
109
|
+
// [...rsaCertificateTypes, ...eccCertificateTypes];
|
|
110
|
+
...rsaCertificateTypesArray
|
|
111
|
+
]; // FIXME: ECC is not yet supported
|
|
112
|
+
|
|
113
|
+
// c8 ignore next
|
|
180
114
|
if (!(this.userTokenGroup instanceof CertificateManager)) {
|
|
181
115
|
errorLog(
|
|
182
116
|
"Expecting this.userTokenGroup to be instanceof CertificateManager :",
|
|
183
|
-
(this.userTokenGroup as
|
|
117
|
+
(this.userTokenGroup as unknown as { constructor: { name: string } }).constructor.name
|
|
184
118
|
);
|
|
185
119
|
throw new Error("Expecting this.userTokenGroup to be instanceof CertificateManager ");
|
|
186
120
|
}
|
|
187
121
|
}
|
|
188
122
|
if (this.applicationGroup) {
|
|
189
|
-
this.
|
|
123
|
+
this._context.map.DefaultApplicationGroup = this.applicationGroup;
|
|
124
|
+
// Store allowed certificate types, or use all known types as default
|
|
125
|
+
this._context.certificateTypes.DefaultApplicationGroup = options.applicationGroupCertificateTypes || [
|
|
126
|
+
// [...rsaCertificateTypes, ...eccCertificateTypes];
|
|
127
|
+
...rsaCertificateTypesArray
|
|
128
|
+
]; // FIXME: ECC is not yet supported
|
|
190
129
|
assert(this.applicationGroup instanceof CertificateManager);
|
|
191
130
|
}
|
|
192
131
|
if (this.httpsGroup) {
|
|
193
|
-
this.
|
|
132
|
+
this._context.map.DefaultHttpsGroup = this.httpsGroup;
|
|
133
|
+
// Store allowed certificate types, or use all known types as default
|
|
134
|
+
this._context.certificateTypes.DefaultHttpsGroup = options.httpsGroupCertificateTypes || [
|
|
135
|
+
// [...rsaCertificateTypes, ...eccCertificateTypes];
|
|
136
|
+
...rsaCertificateTypesArray
|
|
137
|
+
]; // FIXME: ECC is not yet supported
|
|
194
138
|
assert(this.httpsGroup instanceof CertificateManager);
|
|
195
139
|
}
|
|
196
140
|
}
|
|
@@ -213,7 +157,7 @@ export class PushCertificateManagerServerImpl extends EventEmitter implements Pu
|
|
|
213
157
|
}
|
|
214
158
|
|
|
215
159
|
public async getSupportedPrivateKeyFormats(): Promise<string[]> {
|
|
216
|
-
return
|
|
160
|
+
return ["PEM"];
|
|
217
161
|
}
|
|
218
162
|
|
|
219
163
|
public async createSigningRequest(
|
|
@@ -223,157 +167,20 @@ export class PushCertificateManagerServerImpl extends EventEmitter implements Pu
|
|
|
223
167
|
regeneratePrivateKey?: boolean,
|
|
224
168
|
nonce?: Buffer
|
|
225
169
|
): Promise<CreateSigningRequestResult> {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (!subjectName) {
|
|
236
|
-
// reuse existing subjectName
|
|
237
|
-
const currentCertificateFilename = path.join(certificateManager.rootDir, "own/certs/certificate.pem");
|
|
238
|
-
if (!fs.existsSync(currentCertificateFilename)) {
|
|
239
|
-
errorLog("Cannot find existing certificate to extract subjectName", currentCertificateFilename);
|
|
240
|
-
return {
|
|
241
|
-
statusCode: StatusCodes.BadInvalidState
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
const certificate = readCertificate(currentCertificateFilename);
|
|
245
|
-
const e = exploreCertificate(certificate);
|
|
246
|
-
subjectName = subjectToString(e.tbsCertificate.subject);
|
|
247
|
-
warningLog("reusing existing certificate subjectAltName = ", subjectName);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// todo : at this time regenerate private key is not supported
|
|
251
|
-
if (regeneratePrivateKey) {
|
|
252
|
-
// The Server shall create a new Private Key which it stores until the
|
|
253
|
-
// matching signed Certificate is uploaded with the UpdateCertificate Method.
|
|
254
|
-
// Previously created Private Keys may be discarded if UpdateCertificate was not
|
|
255
|
-
// called before calling this method again.
|
|
256
|
-
|
|
257
|
-
// Additional entropy which the caller shall provide if regeneratePrivateKey is TRUE.
|
|
258
|
-
// It shall be at least 32 bytes long
|
|
259
|
-
if (!nonce || nonce.length < 32) {
|
|
260
|
-
make_warningLog(
|
|
261
|
-
" nonce should be provided when regeneratePrivateKey is set, and length shall be greater than 32 bytes"
|
|
262
|
-
);
|
|
263
|
-
return {
|
|
264
|
-
statusCode: StatusCodes.BadInvalidArgument
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const location = path.join(certificateManager.rootDir, "tmp");
|
|
269
|
-
if (fs.existsSync(location)) {
|
|
270
|
-
await rimraf.rimraf(path.join(location));
|
|
271
|
-
}
|
|
272
|
-
if (!fs.existsSync(location)) {
|
|
273
|
-
await fs.promises.mkdir(location);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const destCertificateManager = certificateManager;
|
|
277
|
-
const keySize = (certificateManager as any).keySize; // because keySize is private !
|
|
278
|
-
certificateManager = new CertificateManager({
|
|
279
|
-
keySize,
|
|
280
|
-
location
|
|
281
|
-
});
|
|
282
|
-
debugLog("generating a new private key ...");
|
|
283
|
-
await certificateManager.initialize();
|
|
284
|
-
|
|
285
|
-
this._tmpCertificateManager = certificateManager;
|
|
286
|
-
|
|
287
|
-
this.addPendingTask(async () => {
|
|
288
|
-
await moveFileWithBackup(certificateManager!.privateKey, destCertificateManager.privateKey);
|
|
289
|
-
});
|
|
290
|
-
this.addPendingTask(async () => {
|
|
291
|
-
await rimraf.rimraf(path.join(location));
|
|
292
|
-
});
|
|
293
|
-
} else {
|
|
294
|
-
// The Server uses its existing Private Key
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (typeof subjectName !== "string") {
|
|
298
|
-
return { statusCode: StatusCodes.BadInternalError };
|
|
299
|
-
}
|
|
300
|
-
const options = {
|
|
301
|
-
applicationUri: this.applicationUri,
|
|
302
|
-
subject: subjectName!
|
|
303
|
-
};
|
|
304
|
-
await certificateManager.initialize();
|
|
305
|
-
const csrFile = await certificateManager.createCertificateRequest(options);
|
|
306
|
-
const csrPEM = await readFile(csrFile, "utf8");
|
|
307
|
-
const certificateSigningRequest = convertPEMtoDER(csrPEM);
|
|
308
|
-
|
|
309
|
-
this.addPendingTask(() => deleteFile(csrFile));
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
certificateSigningRequest,
|
|
313
|
-
statusCode: StatusCodes.Good
|
|
314
|
-
};
|
|
170
|
+
return await executeCreateSigningRequest(
|
|
171
|
+
this._context,
|
|
172
|
+
certificateGroupId,
|
|
173
|
+
certificateTypeId,
|
|
174
|
+
subjectName,
|
|
175
|
+
regeneratePrivateKey,
|
|
176
|
+
nonce
|
|
177
|
+
);
|
|
315
178
|
}
|
|
316
179
|
|
|
317
180
|
public async getRejectedList(): Promise<GetRejectedListResult> {
|
|
318
|
-
|
|
319
|
-
filename: string;
|
|
320
|
-
stat: {
|
|
321
|
-
mtime: Date;
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// rejectedList comes from each group
|
|
326
|
-
async function extractRejectedList(group: CertificateManager | undefined, certificateList: FileData[]): Promise<void> {
|
|
327
|
-
if (!group) {
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
const rejectedFolder = path.join(group.rootDir, "rejected");
|
|
331
|
-
const files = await readdir(rejectedFolder);
|
|
332
|
-
|
|
333
|
-
const stat = fs.promises.stat;
|
|
334
|
-
|
|
335
|
-
const promises1: Promise<fs.Stats>[] = [];
|
|
336
|
-
for (const certFile of files) {
|
|
337
|
-
// read date
|
|
338
|
-
promises1.push(stat(path.join(rejectedFolder, certFile)));
|
|
339
|
-
}
|
|
340
|
-
const stats = await Promise.all(promises1);
|
|
341
|
-
|
|
342
|
-
for (let i = 0; i < stats.length; i++) {
|
|
343
|
-
certificateList.push({
|
|
344
|
-
filename: path.join(rejectedFolder, files[i]),
|
|
345
|
-
stat: stats[i]
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const list: FileData[] = [];
|
|
351
|
-
await extractRejectedList(this.applicationGroup, list);
|
|
352
|
-
await extractRejectedList(this.userTokenGroup, list);
|
|
353
|
-
await extractRejectedList(this.httpsGroup, list);
|
|
354
|
-
|
|
355
|
-
// now sort list from newer file to older file
|
|
356
|
-
list.sort((a: FileData, b: FileData) => b.stat.mtime.getTime() - a.stat.mtime.getTime());
|
|
357
|
-
|
|
358
|
-
const promises: Promise<string>[] = [];
|
|
359
|
-
for (const item of list) {
|
|
360
|
-
promises.push(readFile(item.filename, "utf8"));
|
|
361
|
-
}
|
|
362
|
-
const certificatesPEM: string[] = await Promise.all(promises);
|
|
363
|
-
|
|
364
|
-
const certificates: Buffer[] = certificatesPEM.map(convertPEMtoDER);
|
|
365
|
-
return {
|
|
366
|
-
certificates,
|
|
367
|
-
statusCode: StatusCodes.Good
|
|
368
|
-
};
|
|
181
|
+
return await executeGetRejectedList(this._context);
|
|
369
182
|
}
|
|
370
183
|
|
|
371
|
-
public async updateCertificate(
|
|
372
|
-
certificateGroupId: NodeId | string,
|
|
373
|
-
certificateTypeId: NodeId | string,
|
|
374
|
-
certificate: Buffer,
|
|
375
|
-
issuerCertificates: ByteString[]
|
|
376
|
-
): Promise<UpdateCertificateResult>;
|
|
377
184
|
// eslint-disable-next-line max-statements
|
|
378
185
|
public async updateCertificate(
|
|
379
186
|
certificateGroupId: NodeId | string,
|
|
@@ -383,292 +190,66 @@ export class PushCertificateManagerServerImpl extends EventEmitter implements Pu
|
|
|
383
190
|
privateKeyFormat?: string,
|
|
384
191
|
privateKey?: Buffer | string
|
|
385
192
|
): Promise<UpdateCertificateResult> {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
debugLog(" cannot find group ", certificateGroupId);
|
|
396
|
-
return {
|
|
397
|
-
statusCode: StatusCodes.BadInvalidArgument,
|
|
398
|
-
applyChangesRequired: false
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
async function preInstallCertificate(self: PushCertificateManagerServerImpl) {
|
|
403
|
-
const certFolder = path.join(certificateManager.rootDir, "own/certs");
|
|
404
|
-
const certificateFileDER = path.join(certFolder, `_pending_certificate${fileCounter++}.der`);
|
|
405
|
-
const certificateFilePEM = path.join(certFolder, `_pending_certificate${fileCounter++}.pem`);
|
|
406
|
-
|
|
407
|
-
await writeFile(certificateFileDER, certificate, "binary");
|
|
408
|
-
await writeFile(certificateFilePEM, toPem(certificate, "CERTIFICATE"));
|
|
409
|
-
|
|
410
|
-
const destDER = path.join(certFolder, "certificate.der");
|
|
411
|
-
const destPEM = path.join(certFolder, "certificate.pem");
|
|
412
|
-
|
|
413
|
-
// put existing file in security by backing them up
|
|
414
|
-
self.addPendingTask(() => moveFileWithBackup(certificateFileDER, destDER));
|
|
415
|
-
self.addPendingTask(() => moveFileWithBackup(certificateFilePEM, destPEM));
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
async function preInstallPrivateKey(self: PushCertificateManagerServerImpl) {
|
|
419
|
-
assert(privateKeyFormat!.toUpperCase() === "PEM");
|
|
420
|
-
|
|
421
|
-
const ownPrivateFolder = path.join(certificateManager.rootDir, "own/private");
|
|
422
|
-
const privateKeyFilePEM = path.join(ownPrivateFolder, `_pending_private_key${fileCounter++}.pem`);
|
|
423
|
-
|
|
424
|
-
if (privateKey) {
|
|
425
|
-
const privateKey1 = coercePEMorDerToPrivateKey(privateKey);
|
|
426
|
-
const privateKeyPEM = await coercePrivateKeyPem(privateKey1);
|
|
427
|
-
await writeFile(privateKeyFilePEM, privateKeyPEM, "utf-8");
|
|
428
|
-
self.addPendingTask(() => moveFileWithBackup(privateKeyFilePEM, certificateManager.privateKey));
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// OPC Unified Architecture, Part 12 42 Release 1.04:
|
|
433
|
-
//
|
|
434
|
-
// UpdateCertificate is used to update a Certificate for a Server.
|
|
435
|
-
// There are the following three use cases for this Method:
|
|
436
|
-
//
|
|
437
|
-
// - The new Certificate was created based on a signing request created with the Method
|
|
438
|
-
// In this case there is no privateKey provided.
|
|
439
|
-
// - A new privateKey and Certificate was created outside the Server and both are updated
|
|
440
|
-
// with this Method.
|
|
441
|
-
// - A new Certificate was created and signed with the information from the old Certificate.
|
|
442
|
-
// In this case there is no privateKey provided.
|
|
443
|
-
|
|
444
|
-
// The Server shall do all normal integrity checks on the Certificate and all of the issuer
|
|
445
|
-
// Certificates. If errors occur the BadSecurityChecksFailed error is returned.
|
|
446
|
-
// todo : all normal integrity check on the certificate
|
|
447
|
-
const certInfo = exploreCertificate(certificate);
|
|
448
|
-
|
|
449
|
-
const now = new Date();
|
|
450
|
-
if (certInfo.tbsCertificate.validity.notBefore.getTime() > now.getTime()) {
|
|
451
|
-
// certificate is not yet valid
|
|
452
|
-
warningLog(
|
|
453
|
-
"Certificate is not yet valid : not before ",
|
|
454
|
-
certInfo.tbsCertificate.validity.notBefore.toISOString(),
|
|
455
|
-
"now = ",
|
|
456
|
-
now.toISOString()
|
|
457
|
-
);
|
|
458
|
-
return {
|
|
459
|
-
statusCode: StatusCodes.BadSecurityChecksFailed,
|
|
460
|
-
applyChangesRequired: false
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
if (certInfo.tbsCertificate.validity.notAfter.getTime() < now.getTime()) {
|
|
464
|
-
// certificate is already out of date
|
|
465
|
-
warningLog(
|
|
466
|
-
"Certificate is already out of date : not after ",
|
|
467
|
-
certInfo.tbsCertificate.validity.notAfter.toISOString(),
|
|
468
|
-
"now = ",
|
|
469
|
-
now.toISOString()
|
|
470
|
-
);
|
|
471
|
-
return {
|
|
472
|
-
statusCode: StatusCodes.BadSecurityChecksFailed,
|
|
473
|
-
applyChangesRequired: false
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// If the Server returns applyChangesRequired=FALSE then it is indicating that it is able to
|
|
478
|
-
// satisfy the requirements specified for the ApplyChanges Method.
|
|
479
|
-
|
|
480
|
-
debugLog(" updateCertificate ", makeSHA1Thumbprint(certificate).toString("hex"));
|
|
481
|
-
|
|
482
|
-
if (!privateKeyFormat || !privateKey) {
|
|
483
|
-
// first of all we need to find the future private key;
|
|
484
|
-
// this one may have been created during the creation of the certificate signing request
|
|
485
|
-
// but is not active yet
|
|
486
|
-
const privateKey1 = readPrivateKey(
|
|
487
|
-
this._tmpCertificateManager ? this._tmpCertificateManager.privateKey : certificateManager.privateKey
|
|
488
|
-
);
|
|
489
|
-
|
|
490
|
-
// The Server shall report an error if the public key does not match the existing Certificate and
|
|
491
|
-
// the privateKey was not provided.
|
|
492
|
-
// privateKey is not provided, so check that the public key matches the existing certificate
|
|
493
|
-
if (!certificateMatchesPrivateKey(certificate, privateKey1)) {
|
|
494
|
-
// certificate doesn't match privateKey
|
|
495
|
-
warningLog("certificate doesn't match privateKey");
|
|
496
|
-
/* debug code */
|
|
497
|
-
const certificatePEM = toPem(certificate, "CERTIFICATE");
|
|
498
|
-
certificatePEM;
|
|
499
|
-
//xx const privateKeyPEM = toPem(privateKeyDER, "RSA PRIVATE KEY");
|
|
500
|
-
//xx const initialBuffer = Buffer.from("Lorem Ipsum");
|
|
501
|
-
//xx const encryptedBuffer = publicEncrypt_long(initialBuffer, certificatePEM, 256, 11);
|
|
502
|
-
//xx const decryptedBuffer = privateDecrypt_long(encryptedBuffer, privateKeyPEM, 256);
|
|
503
|
-
return {
|
|
504
|
-
statusCode: StatusCodes.BadSecurityChecksFailed,
|
|
505
|
-
applyChangesRequired: false,
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
// a new certificate is provided for us,
|
|
509
|
-
// we keep our private key
|
|
510
|
-
// we do this in two stages
|
|
511
|
-
await preInstallCertificate(this);
|
|
512
|
-
|
|
513
|
-
return {
|
|
514
|
-
statusCode: StatusCodes.Good,
|
|
515
|
-
applyChangesRequired: true,
|
|
516
|
-
};
|
|
517
|
-
} else if (privateKey) {
|
|
518
|
-
// a private key has been provided by the caller !
|
|
519
|
-
if (!privateKeyFormat) {
|
|
520
|
-
warningLog("the privateKeyFormat must be specified " + privateKeyFormat);
|
|
521
|
-
return { statusCode: StatusCodes.BadNotSupported, applyChangesRequired: false};
|
|
522
|
-
}
|
|
523
|
-
if (privateKeyFormat !== "PEM" && privateKeyFormat !== "PFX") {
|
|
524
|
-
warningLog(" the private key format is invalid privateKeyFormat =" + privateKeyFormat);
|
|
525
|
-
return { statusCode: StatusCodes.BadNotSupported, applyChangesRequired: false };
|
|
526
|
-
}
|
|
527
|
-
if (privateKeyFormat !== "PEM") {
|
|
528
|
-
warningLog("in NodeOPCUA we only support PEM for the moment privateKeyFormat =" + privateKeyFormat);
|
|
529
|
-
return { statusCode: StatusCodes.BadNotSupported , applyChangesRequired: false };
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
let privateKey1: PrivateKey | undefined;
|
|
533
|
-
if (privateKey && (privateKey instanceof Buffer || typeof privateKey === "string")) {
|
|
534
|
-
if (privateKey instanceof Buffer) {
|
|
535
|
-
assert(privateKeyFormat === "PEM");
|
|
536
|
-
privateKey = privateKey.toString("utf-8");
|
|
537
|
-
}
|
|
538
|
-
privateKey1 = coercePEMorDerToPrivateKey(privateKey);
|
|
539
|
-
}
|
|
540
|
-
if (!privateKey1) {
|
|
541
|
-
return { statusCode: StatusCodes.BadNotSupported, applyChangesRequired: false };
|
|
542
|
-
}
|
|
543
|
-
// privateKey is provided, so check that the public key matches provided private key
|
|
544
|
-
if (!certificateMatchesPrivateKey(certificate, privateKey1!)) {
|
|
545
|
-
// certificate doesn't match privateKey
|
|
546
|
-
warningLog("certificate doesn't match privateKey");
|
|
547
|
-
return { statusCode: StatusCodes.BadSecurityChecksFailed, applyChangesRequired: false };
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
await preInstallPrivateKey(this);
|
|
551
|
-
|
|
552
|
-
await preInstallCertificate(this);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
return {
|
|
557
|
-
statusCode: StatusCodes.Good,
|
|
558
|
-
applyChangesRequired: true
|
|
559
|
-
};
|
|
560
|
-
} else {
|
|
561
|
-
// todo !
|
|
562
|
-
return {
|
|
563
|
-
statusCode: StatusCodes.BadNotSupported,
|
|
564
|
-
applyChangesRequired: true
|
|
565
|
-
};
|
|
566
|
-
}
|
|
193
|
+
return await executeUpdateCertificate(
|
|
194
|
+
this._context,
|
|
195
|
+
certificateGroupId,
|
|
196
|
+
certificateTypeId,
|
|
197
|
+
certificate,
|
|
198
|
+
issuerCertificates,
|
|
199
|
+
privateKeyFormat,
|
|
200
|
+
privateKey
|
|
201
|
+
);
|
|
567
202
|
}
|
|
568
203
|
|
|
569
204
|
/**
|
|
570
|
-
*
|
|
571
|
-
* ApplyChanges is used to apply pending Certificate and TrustList updates
|
|
205
|
+
*
|
|
206
|
+
* ApplyChanges is used to apply pending Certificate and TrustList updates
|
|
572
207
|
* and to complete a transaction as described in 7.10.2.
|
|
573
|
-
*
|
|
208
|
+
*
|
|
574
209
|
* ApplyChanges returns Bad_InvalidState if any TrustList is still open for writing.
|
|
575
210
|
* No changes are applied and ApplyChanges can be called again after the TrustList is closed.
|
|
576
|
-
*
|
|
211
|
+
*
|
|
577
212
|
* If a Session is closed or abandoned then the transaction is closed and all pending changes are discarded.
|
|
578
|
-
*
|
|
579
|
-
* If ApplyChanges is called and there is no active transaction then the Server returns Bad_NothingToDo.
|
|
213
|
+
*
|
|
214
|
+
* If ApplyChanges is called and there is no active transaction then the Server returns Bad_NothingToDo.
|
|
580
215
|
* If there is an active transaction, however, no changes are pending the result is Good and the transaction is closed.
|
|
581
|
-
*
|
|
216
|
+
*
|
|
582
217
|
* When a Server Certificate or TrustList changes active SecureChannels are not immediately affected.
|
|
583
|
-
* This ensures the caller of ApplyChanges can get a response to the Method call.
|
|
584
|
-
* Once the Method response is returned the Server shall force existing SecureChannels affected by
|
|
218
|
+
* This ensures the caller of ApplyChanges can get a response to the Method call.
|
|
219
|
+
* Once the Method response is returned the Server shall force existing SecureChannels affected by
|
|
585
220
|
* the changes to renegotiate and use the new Server Certificate and/or TrustLists.
|
|
586
|
-
*
|
|
587
|
-
* Servers may close SecureChannels without discarding any Sessions or Subscriptions.
|
|
588
|
-
* This will seem like a network interruption from the perspective of the Client and the Client reconnect
|
|
589
|
-
* logic (see OPC 10000-4) allows them to recover their Session and Subscriptions.
|
|
221
|
+
*
|
|
222
|
+
* Servers may close SecureChannels without discarding any Sessions or Subscriptions.
|
|
223
|
+
* This will seem like a network interruption from the perspective of the Client and the Client reconnect
|
|
224
|
+
* logic (see OPC 10000-4) allows them to recover their Session and Subscriptions.
|
|
590
225
|
* Note that some Clients may not be able to reconnect because they are no longer trusted.
|
|
591
|
-
*
|
|
226
|
+
*
|
|
592
227
|
* Other Servers may need to do a complete shutdown.
|
|
593
|
-
*
|
|
594
|
-
* In this case, the Server shall advertise its intent to interrupt connections by setting the
|
|
228
|
+
*
|
|
229
|
+
* In this case, the Server shall advertise its intent to interrupt connections by setting the
|
|
595
230
|
* SecondsTillShutdown and ShutdownReason Properties in the ServerStatus Variable.
|
|
596
|
-
*
|
|
597
|
-
* If a TrustList change only affects UserIdentity associated with a Session then Servers
|
|
598
|
-
* shall re-evaluate the UserIdentity and if it is no longer valid the Session and associated
|
|
231
|
+
*
|
|
232
|
+
* If a TrustList change only affects UserIdentity associated with a Session then Servers
|
|
233
|
+
* shall re-evaluate the UserIdentity and if it is no longer valid the Session and associated
|
|
599
234
|
* Subscriptions are closed.
|
|
600
|
-
*
|
|
601
|
-
* This Method shall be called from an authenticated SecureChannel and from the Session that
|
|
235
|
+
*
|
|
236
|
+
* This Method shall be called from an authenticated SecureChannel and from the Session that
|
|
602
237
|
* created the transaction and has access to the SecurityAdmin Role (see 7.2).
|
|
603
|
-
*
|
|
238
|
+
*
|
|
604
239
|
*/
|
|
605
|
-
public async applyChanges(): Promise<StatusCode> {
|
|
606
|
-
|
|
607
|
-
// This Method should only be called if a previous call to a Method that changed the
|
|
608
|
-
// configuration returns applyChangesRequired=true.
|
|
609
|
-
//
|
|
610
|
-
// If the Server Certificate has changed, Secure Channels using the old Certificate will
|
|
611
|
-
// eventually be interrupted.
|
|
612
|
-
|
|
613
|
-
this.emit("CertificateAboutToChange", this.$$actionQueue);
|
|
614
|
-
await this.flushActionQueue();
|
|
615
|
-
|
|
616
|
-
try {
|
|
617
|
-
await this.applyPendingTasks();
|
|
618
|
-
} catch (err) {
|
|
619
|
-
debugLog("err ", err);
|
|
620
|
-
return StatusCodes.BadInternalError;
|
|
621
|
-
}
|
|
622
|
-
this.emit("CertificateChanged", this.$$actionQueue);
|
|
623
|
-
await this.flushActionQueue();
|
|
624
|
-
|
|
625
|
-
// The only leeway the Server has is with the timing.
|
|
626
|
-
// In the best case, the Server can close the TransportConnections for the affected Endpoints and leave any
|
|
627
|
-
// Subscriptions intact. This should appear no different than a network interruption from the
|
|
628
|
-
// perspective of the Client. The Client should be prepared to deal with Certificate changes
|
|
629
|
-
// during its reconnect logic. In the worst case, a full shutdown which affects all connected
|
|
630
|
-
// Clients will be necessary. In the latter case, the Server shall advertise its intent to interrupt
|
|
631
|
-
// connections by setting the SecondsTillShutdown and ShutdownReason Properties in the
|
|
632
|
-
// ServerStatus Variable.
|
|
633
|
-
|
|
634
|
-
// If the Secure Channel being used to call this Method will be affected by the Certificate change
|
|
635
|
-
// then the Server shall introduce a delay long enough to allow the caller to receive a reply.
|
|
636
|
-
return StatusCodes.Good;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
private getCertificateManager(certificateGroupId: NodeId | string): CertificateManager | null {
|
|
640
|
-
const groupName = findCertificateGroupName(certificateGroupId);
|
|
641
|
-
return this._map[groupName] || null;
|
|
240
|
+
public async applyChanges(context?: ISessionContext): Promise<StatusCode> {
|
|
241
|
+
return await executeApplyChanges(this._context, context);
|
|
642
242
|
}
|
|
643
243
|
|
|
644
|
-
|
|
645
|
-
this.
|
|
244
|
+
public getCertificateManager(groupName: string): CertificateManager | null {
|
|
245
|
+
return this._context.map[groupName] || null;
|
|
646
246
|
}
|
|
647
247
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
const promises: Promise<void>[] = [];
|
|
651
|
-
const t = this._pendingTasks.splice(0);
|
|
652
|
-
|
|
653
|
-
if (false) {
|
|
654
|
-
// node 10.2 and above
|
|
655
|
-
for await (const task of t) {
|
|
656
|
-
await task();
|
|
657
|
-
}
|
|
658
|
-
} else {
|
|
659
|
-
while (t.length) {
|
|
660
|
-
const task = t.shift()!;
|
|
661
|
-
await task();
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
await Promise.all(promises);
|
|
665
|
-
debugLog("end applyPendingTasks");
|
|
248
|
+
public getCertificateTypes(groupName: string): NodeId[] | undefined {
|
|
249
|
+
return this._context.certificateTypes[groupName];
|
|
666
250
|
}
|
|
667
251
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const first = this.$$actionQueue.pop()!;
|
|
671
|
-
await first!();
|
|
672
|
-
}
|
|
252
|
+
public async dispose(): Promise<void> {
|
|
253
|
+
await this._context.dispose();
|
|
673
254
|
}
|
|
674
255
|
}
|