node-opcua-server 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/addressSpace_accessor.js +7 -5
- package/dist/addressSpace_accessor.js.map +1 -1
- package/dist/base_server.d.ts +95 -18
- package/dist/base_server.js +217 -69
- package/dist/base_server.js.map +1 -1
- package/dist/monitored_item.js +32 -32
- package/dist/monitored_item.js.map +1 -1
- package/dist/node_sampler.js +1 -1
- package/dist/node_sampler.js.map +1 -1
- package/dist/opcua_server.d.ts +171 -123
- package/dist/opcua_server.js +466 -223
- package/dist/opcua_server.js.map +1 -1
- package/dist/register_server_manager.d.ts +2 -2
- package/dist/register_server_manager.js +12 -6
- package/dist/register_server_manager.js.map +1 -1
- package/dist/register_server_manager_mdns_only.js +1 -1
- package/dist/register_server_manager_mdns_only.js.map +1 -1
- package/dist/server_end_point.d.ts +108 -12
- package/dist/server_end_point.js +157 -57
- package/dist/server_end_point.js.map +1 -1
- package/dist/server_engine.d.ts +32 -14
- package/dist/server_engine.js +160 -59
- package/dist/server_engine.js.map +1 -1
- package/dist/server_publish_engine.js +5 -5
- package/dist/server_publish_engine.js.map +1 -1
- package/dist/server_session.js +4 -4
- package/dist/server_session.js.map +1 -1
- package/dist/server_subscription.js +18 -18
- package/dist/server_subscription.js.map +1 -1
- package/dist/sessions_compatible_for_transfer.js +1 -1
- package/dist/sessions_compatible_for_transfer.js.map +1 -1
- package/package.json +47 -47
- package/source/addressSpace_accessor.ts +6 -3
- package/source/base_server.ts +252 -90
- package/source/monitored_item.ts +32 -32
- package/source/node_sampler.ts +1 -1
- package/source/opcua_server.ts +674 -489
- package/source/register_server_manager.ts +11 -5
- package/source/register_server_manager_mdns_only.ts +1 -1
- package/source/server_end_point.ts +278 -94
- package/source/server_engine.ts +246 -135
- package/source/server_publish_engine.ts +5 -5
- package/source/server_session.ts +4 -4
- package/source/server_subscription.ts +18 -18
- package/source/sessions_compatible_for_transfer.ts +4 -2
package/source/opcua_server.ts
CHANGED
|
@@ -2,76 +2,74 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @module node-opcua-server
|
|
4
4
|
*/
|
|
5
|
-
import { randomBytes } from "crypto";
|
|
6
|
-
import {
|
|
7
|
-
import { callbackify, types } from "util";
|
|
5
|
+
import { randomBytes } from "node:crypto";
|
|
6
|
+
import { callbackify, types } from "node:util";
|
|
8
7
|
|
|
9
|
-
import async from "async";
|
|
10
8
|
import chalk from "chalk";
|
|
11
|
-
|
|
12
|
-
import { extractFullyQualifiedDomainName, getFullyQualifiedDomainName } from "node-opcua-hostname";
|
|
13
|
-
|
|
14
|
-
import { assert } from "node-opcua-assert";
|
|
15
|
-
import { isNullOrUndefined } from "node-opcua-utils";
|
|
16
|
-
|
|
17
9
|
import {
|
|
18
|
-
AddressSpace,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
PseudoVariantExtensionObject,
|
|
24
|
-
PseudoVariantExtensionObjectArray,
|
|
25
|
-
PseudoVariantLocalizedText,
|
|
26
|
-
PseudoVariantNodeId,
|
|
27
|
-
PseudoVariantString,
|
|
28
|
-
RaiseEventData,
|
|
29
|
-
SessionContext,
|
|
30
|
-
UAObject,
|
|
31
|
-
UAVariable,
|
|
32
|
-
ISessionContext,
|
|
33
|
-
UAView,
|
|
34
|
-
EventTypeLike,
|
|
35
|
-
UAObjectType,
|
|
36
|
-
PseudoVariantStringPredefined,
|
|
10
|
+
type AddressSpace,
|
|
11
|
+
type EventTypeLike,
|
|
12
|
+
type IRolePolicyOverride,
|
|
13
|
+
type IServerBase,
|
|
14
|
+
type ISessionContext,
|
|
37
15
|
innerBrowse,
|
|
38
16
|
innerBrowseNext,
|
|
39
|
-
|
|
17
|
+
type PseudoVariant,
|
|
18
|
+
type PseudoVariantBoolean,
|
|
19
|
+
type PseudoVariantByteString,
|
|
20
|
+
type PseudoVariantDateTime,
|
|
21
|
+
type PseudoVariantDuration,
|
|
22
|
+
type PseudoVariantExtensionObject,
|
|
23
|
+
type PseudoVariantExtensionObjectArray,
|
|
24
|
+
type PseudoVariantLocalizedText,
|
|
25
|
+
type PseudoVariantNodeId,
|
|
26
|
+
type PseudoVariantString,
|
|
27
|
+
type PseudoVariantStringPredefined,
|
|
28
|
+
type RaiseEventData,
|
|
29
|
+
SessionContext,
|
|
30
|
+
type UAEventType,
|
|
31
|
+
type UAObject,
|
|
32
|
+
type UAObjectType,
|
|
33
|
+
type UAVariable,
|
|
34
|
+
type UAView
|
|
40
35
|
} from "node-opcua-address-space";
|
|
41
|
-
import {
|
|
36
|
+
import { assert } from "node-opcua-assert";
|
|
37
|
+
import type { ByteString, UAString } from "node-opcua-basic-types";
|
|
38
|
+
import { getDefaultCertificateManager, type OPCUACertificateManager } from "node-opcua-certificate-manager";
|
|
42
39
|
import { ServerState } from "node-opcua-common";
|
|
43
|
-
import { Certificate, exploreCertificate, Nonce } from "node-opcua-crypto/web";
|
|
40
|
+
import { type Certificate, exploreCertificate, type Nonce } from "node-opcua-crypto/web";
|
|
44
41
|
import {
|
|
45
42
|
AttributeIds,
|
|
46
43
|
filterDiagnosticOperationLevel,
|
|
47
44
|
filterDiagnosticServiceLevel,
|
|
45
|
+
LocalizedText,
|
|
48
46
|
NodeClass,
|
|
49
47
|
RESPONSE_DIAGNOSTICS_MASK_ALL
|
|
50
48
|
} from "node-opcua-data-model";
|
|
51
49
|
import { DataValue } from "node-opcua-data-value";
|
|
52
50
|
import { dump, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
|
|
53
|
-
import {
|
|
51
|
+
import { extractFullyQualifiedDomainName, getFullyQualifiedDomainName, isIPAddress } from "node-opcua-hostname";
|
|
52
|
+
import type { NodeId } from "node-opcua-nodeid";
|
|
54
53
|
import { ObjectRegistry } from "node-opcua-object-registry";
|
|
55
54
|
import {
|
|
56
|
-
AsymmetricAlgorithmSecurityHeader,
|
|
57
55
|
coerceSecurityPolicy,
|
|
58
56
|
computeSignature,
|
|
59
57
|
fromURI,
|
|
60
58
|
getCryptoFactory,
|
|
61
|
-
Message,
|
|
59
|
+
type Message,
|
|
62
60
|
MessageSecurityMode,
|
|
63
61
|
nonceAlreadyBeenUsed,
|
|
64
|
-
Request,
|
|
65
|
-
Response,
|
|
62
|
+
type Request,
|
|
63
|
+
type Response,
|
|
66
64
|
SecurityPolicy,
|
|
67
|
-
ServerSecureChannelLayer,
|
|
68
|
-
SignatureData,
|
|
65
|
+
type ServerSecureChannelLayer,
|
|
66
|
+
type SignatureData,
|
|
69
67
|
verifySignature
|
|
70
68
|
} from "node-opcua-secure-channel";
|
|
71
69
|
import { BrowseNextRequest, BrowseNextResponse, BrowseRequest, BrowseResponse } from "node-opcua-service-browse";
|
|
72
70
|
import { CallRequest, CallResponse } from "node-opcua-service-call";
|
|
73
71
|
import { ApplicationType, UserTokenType } from "node-opcua-service-endpoints";
|
|
74
|
-
import { HistoryReadRequest, HistoryReadResponse, HistoryReadResult, HistoryUpdateResponse } from "node-opcua-service-history";
|
|
72
|
+
import { HistoryReadRequest, HistoryReadResponse, type HistoryReadResult, HistoryUpdateResponse } from "node-opcua-service-history";
|
|
75
73
|
import {
|
|
76
74
|
AddNodesResponse,
|
|
77
75
|
AddReferencesResponse,
|
|
@@ -129,57 +127,48 @@ import {
|
|
|
129
127
|
TranslateBrowsePathsToNodeIdsResponse
|
|
130
128
|
} from "node-opcua-service-translate-browse-path";
|
|
131
129
|
import { WriteRequest, WriteResponse } from "node-opcua-service-write";
|
|
132
|
-
import { CallbackT,
|
|
130
|
+
import { type CallbackT, StatusCode, StatusCodes } from "node-opcua-status-code";
|
|
133
131
|
import {
|
|
134
|
-
ApplicationDescriptionOptions,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
type ApplicationDescriptionOptions,
|
|
133
|
+
type BrowseDescriptionOptions,
|
|
134
|
+
type BrowseResult,
|
|
135
|
+
type BuildInfo,
|
|
136
|
+
type BuildInfoOptions,
|
|
138
137
|
CancelResponse,
|
|
139
138
|
EndpointDescription,
|
|
140
|
-
MonitoredItemModifyRequest,
|
|
141
|
-
MonitoringMode,
|
|
142
|
-
UserIdentityToken,
|
|
143
|
-
UserTokenPolicy,
|
|
144
|
-
BuildInfoOptions,
|
|
145
|
-
MonitoredItemCreateResult,
|
|
146
139
|
IssuedIdentityToken,
|
|
147
|
-
|
|
140
|
+
type MonitoredItemCreateResult,
|
|
141
|
+
type MonitoredItemModifyRequest,
|
|
142
|
+
MonitoringMode,
|
|
148
143
|
ServiceFault,
|
|
149
|
-
|
|
150
|
-
|
|
144
|
+
type UserIdentityToken,
|
|
145
|
+
type UserTokenPolicy
|
|
151
146
|
} from "node-opcua-types";
|
|
152
|
-
import {
|
|
153
|
-
import { VariantArrayType } from "node-opcua-variant";
|
|
154
|
-
import {
|
|
147
|
+
import { isNullOrUndefined, matchUri } from "node-opcua-utils";
|
|
148
|
+
import { DataType, type Variant, VariantArrayType } from "node-opcua-variant";
|
|
149
|
+
import { withCallback } from "thenify-ex";
|
|
155
150
|
|
|
156
|
-
import {
|
|
157
|
-
import { ObjectIds, ObjectTypeIds } from "node-opcua-constants";
|
|
158
|
-
import { OPCUABaseServer, OPCUABaseServerOptions } from "./base_server";
|
|
151
|
+
import { OPCUABaseServer, type OPCUABaseServerOptions } from "./base_server";
|
|
159
152
|
import { Factory } from "./factory";
|
|
160
|
-
import {
|
|
153
|
+
import type { IChannelData } from "./i_channel_data";
|
|
154
|
+
import type { IRegisterServerManager } from "./i_register_server_manager";
|
|
155
|
+
import type { ISocketData } from "./i_socket_data";
|
|
161
156
|
import { MonitoredItem } from "./monitored_item";
|
|
162
157
|
import { RegisterServerManager } from "./register_server_manager";
|
|
163
158
|
import { RegisterServerManagerHidden } from "./register_server_manager_hidden";
|
|
164
159
|
import { RegisterServerManagerMDNSONLY } from "./register_server_manager_mdns_only";
|
|
165
|
-
import {
|
|
166
|
-
import {
|
|
167
|
-
import {
|
|
168
|
-
import {
|
|
169
|
-
import {
|
|
170
|
-
import {
|
|
171
|
-
import {
|
|
172
|
-
import { UAUserManagerBase, makeUserManager, UserManagerOptions } from "./user_manager";
|
|
160
|
+
import type { SamplingFunc } from "./sampling_func";
|
|
161
|
+
import type { ServerCapabilitiesOptions } from "./server_capabilities";
|
|
162
|
+
import { type AdvertisedEndpoint, type EndpointDescriptionEx, type IServerTransportSettings, OPCUAServerEndPoint, normalizeAdvertisedEndpoints, parseOpcTcpUrl } from "./server_end_point";
|
|
163
|
+
import { type ClosingReason, type CreateSessionOption, ServerEngine } from "./server_engine";
|
|
164
|
+
import type { ServerSession } from "./server_session";
|
|
165
|
+
import type { CreateMonitoredItemHook, DeleteMonitoredItemHook, Subscription } from "./server_subscription";
|
|
166
|
+
import { makeUserManager, type UAUserManagerBase, type UserManagerOptions } from "./user_manager";
|
|
173
167
|
import { bindRoleSet } from "./user_manager_ua";
|
|
174
|
-
import { SamplingFunc } from "./sampling_func";
|
|
175
168
|
|
|
176
169
|
function isSubscriptionIdInvalid(subscriptionId: number): boolean {
|
|
177
170
|
return subscriptionId < 0 || subscriptionId >= 0xffffffff;
|
|
178
171
|
}
|
|
179
|
-
|
|
180
|
-
// tslint:disable-next-line:no-var-requires
|
|
181
|
-
import { withCallback } from "thenify-ex";
|
|
182
|
-
// tslint:disable-next-line:no-var-requires
|
|
183
172
|
const package_info = require("../package.json");
|
|
184
173
|
const debugLog = make_debugLog(__filename);
|
|
185
174
|
const errorLog = make_errorLog(__filename);
|
|
@@ -187,11 +176,16 @@ const warningLog = make_warningLog(__filename);
|
|
|
187
176
|
|
|
188
177
|
const default_maxConnectionsPerEndpoint = 10;
|
|
189
178
|
|
|
190
|
-
function g_sendError(
|
|
179
|
+
function g_sendError(
|
|
180
|
+
channel: ServerSecureChannelLayer,
|
|
181
|
+
message: Message,
|
|
182
|
+
_ResponseClass: ResponseClassType,
|
|
183
|
+
statusCode: StatusCode
|
|
184
|
+
): void {
|
|
191
185
|
const response = new ServiceFault({
|
|
192
186
|
responseHeader: { serviceResult: statusCode }
|
|
193
187
|
});
|
|
194
|
-
|
|
188
|
+
channel.send_response("MSG", response, message);
|
|
195
189
|
}
|
|
196
190
|
|
|
197
191
|
const default_build_info: BuildInfoOptions = {
|
|
@@ -238,7 +232,7 @@ function _adjust_session_timeout(sessionTimeout: number) {
|
|
|
238
232
|
|
|
239
233
|
function channel_has_session(channel: ServerSecureChannelLayer, session: ServerSession): boolean {
|
|
240
234
|
if (session.channel === channel) {
|
|
241
|
-
assert(Object.
|
|
235
|
+
assert(Object.hasOwn(channel.sessionTokens, session.authenticationToken.toString()));
|
|
242
236
|
return true;
|
|
243
237
|
}
|
|
244
238
|
return false;
|
|
@@ -253,13 +247,13 @@ function moveSessionToChannel(session: ServerSession, channel: ServerSecureChann
|
|
|
253
247
|
session._detach_channel();
|
|
254
248
|
session._attach_channel(channel);
|
|
255
249
|
|
|
256
|
-
assert(session.channel
|
|
250
|
+
assert(session.channel?.channelId === channel.channelId);
|
|
257
251
|
}
|
|
258
252
|
|
|
259
253
|
async function _attempt_to_close_some_old_unactivated_session(server: OPCUAServer) {
|
|
260
|
-
const session = server.engine
|
|
254
|
+
const session = server.engine?.getOldestInactiveSession();
|
|
261
255
|
if (session) {
|
|
262
|
-
await server.engine
|
|
256
|
+
await server.engine?.closeSession(session.authenticationToken, false, "Forcing");
|
|
263
257
|
}
|
|
264
258
|
}
|
|
265
259
|
|
|
@@ -282,12 +276,12 @@ function getRequiredEndpointInfo(endpoint: EndpointDescription) {
|
|
|
282
276
|
userIdentityTokens: endpoint.userIdentityTokens
|
|
283
277
|
});
|
|
284
278
|
// reduce even further by explicitly setting unwanted members to null
|
|
285
|
-
e.server.applicationName =
|
|
279
|
+
e.server.applicationName = new LocalizedText({ text: "" });
|
|
286
280
|
// xx e.server.applicationType = null as any;
|
|
287
281
|
e.server.gatewayServerUri = null;
|
|
288
282
|
e.server.discoveryProfileUri = null;
|
|
289
283
|
e.server.discoveryUrls = null;
|
|
290
|
-
e.serverCertificate = null as
|
|
284
|
+
e.serverCertificate = null as unknown as Buffer;
|
|
291
285
|
return e;
|
|
292
286
|
}
|
|
293
287
|
|
|
@@ -301,8 +295,8 @@ function _serverEndpointsForCreateSessionResponse(server: OPCUAServer, endpointU
|
|
|
301
295
|
// https://reference.opcfoundation.org/v104/Core/docs/Part4/5.6.2/
|
|
302
296
|
// https://reference.opcfoundation.org/v105/Core/docs/Part4/5.6.2/
|
|
303
297
|
return server
|
|
304
|
-
.
|
|
305
|
-
.filter((e) => !(e as
|
|
298
|
+
.findMatchingEndpoints(endpointUrl)
|
|
299
|
+
.filter((e) => !(e as unknown as { restricted: boolean }).restricted) // remove restricted endpoints
|
|
306
300
|
.filter((e) => matchUri(e.endpointUrl, endpointUrl))
|
|
307
301
|
.map(getRequiredEndpointInfo);
|
|
308
302
|
}
|
|
@@ -325,7 +319,10 @@ function findUserTokenByPolicy(
|
|
|
325
319
|
policyId: SecurityPolicy | string | null
|
|
326
320
|
): UserTokenPolicy | null {
|
|
327
321
|
assert(endpoint_description instanceof EndpointDescription);
|
|
328
|
-
|
|
322
|
+
if (!endpoint_description.userIdentityTokens) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
const r = endpoint_description.userIdentityTokens.filter(
|
|
329
326
|
(userIdentity: UserTokenPolicy) =>
|
|
330
327
|
userIdentity.tokenType === userTokenType && (!policyId || userIdentity.policyId === policyId)
|
|
331
328
|
);
|
|
@@ -334,7 +331,10 @@ function findUserTokenByPolicy(
|
|
|
334
331
|
|
|
335
332
|
function findUserTokenPolicy(endpoint_description: EndpointDescription, userTokenType: UserTokenType): UserTokenPolicy | null {
|
|
336
333
|
assert(endpoint_description instanceof EndpointDescription);
|
|
337
|
-
|
|
334
|
+
if (!endpoint_description.userIdentityTokens) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
const r = endpoint_description.userIdentityTokens.filter((userIdentity: UserTokenPolicy) => {
|
|
338
338
|
assert(userIdentity.tokenType !== undefined);
|
|
339
339
|
return userIdentity.tokenType === userTokenType;
|
|
340
340
|
});
|
|
@@ -350,7 +350,13 @@ function createAnonymousIdentityToken(endpoint_desc: EndpointDescription) {
|
|
|
350
350
|
return new AnonymousIdentityToken({ policyId: userTokenPolicy.policyId });
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
-
function sameIdentityToken(token1
|
|
353
|
+
function sameIdentityToken(token1?: UserIdentityToken, token2?: UserIdentityToken): boolean {
|
|
354
|
+
if (!token1 && !token2) {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
if (!token1 || !token2) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
354
360
|
if (token1 instanceof UserNameIdentityToken) {
|
|
355
361
|
if (!(token2 instanceof UserNameIdentityToken)) {
|
|
356
362
|
return false;
|
|
@@ -388,7 +394,7 @@ function getTokenType(userIdentityToken: UserIdentityToken): UserTokenType {
|
|
|
388
394
|
}
|
|
389
395
|
return UserTokenType.Invalid;
|
|
390
396
|
}
|
|
391
|
-
function thumbprint(certificate?: Certificate): string {
|
|
397
|
+
function thumbprint(certificate?: Certificate | null): string {
|
|
392
398
|
return certificate ? certificate.toString("base64") : "";
|
|
393
399
|
}
|
|
394
400
|
|
|
@@ -408,7 +414,7 @@ function monitoredItem_read_and_record_value(
|
|
|
408
414
|
context: ISessionContext,
|
|
409
415
|
oldValue: DataValue,
|
|
410
416
|
node: UAVariable,
|
|
411
|
-
itemToMonitor:
|
|
417
|
+
itemToMonitor: ReadValueId,
|
|
412
418
|
callback: (err: Error | null, dataValue?: DataValue) => void
|
|
413
419
|
) {
|
|
414
420
|
assert(self instanceof MonitoredItem);
|
|
@@ -429,7 +435,7 @@ function monitoredItem_read_and_record_value_async(
|
|
|
429
435
|
context: ISessionContext,
|
|
430
436
|
oldValue: DataValue,
|
|
431
437
|
node: UAVariable,
|
|
432
|
-
itemToMonitor:
|
|
438
|
+
itemToMonitor: ReadValueId,
|
|
433
439
|
callback: (err: Error | null, dataValue?: DataValue) => void
|
|
434
440
|
) {
|
|
435
441
|
assert(context instanceof SessionContext);
|
|
@@ -444,12 +450,12 @@ function monitoredItem_read_and_record_value_async(
|
|
|
444
450
|
});
|
|
445
451
|
}
|
|
446
452
|
|
|
447
|
-
function build_scanning_node_function(addressSpace: AddressSpace, itemToMonitor:
|
|
453
|
+
function build_scanning_node_function(addressSpace: AddressSpace, itemToMonitor: ReadValueId): SamplingFunc {
|
|
448
454
|
assert(itemToMonitor instanceof ReadValueId);
|
|
449
455
|
|
|
450
456
|
const node = addressSpace.findNode(itemToMonitor.nodeId) as UAVariable;
|
|
451
457
|
|
|
452
|
-
/*
|
|
458
|
+
/* c8 ignore next */
|
|
453
459
|
if (!node) {
|
|
454
460
|
errorLog(" INVALID NODE ID , ", itemToMonitor.nodeId.toString());
|
|
455
461
|
dump(itemToMonitor);
|
|
@@ -508,7 +514,7 @@ function build_scanning_node_function(addressSpace: AddressSpace, itemToMonitor:
|
|
|
508
514
|
}
|
|
509
515
|
}
|
|
510
516
|
|
|
511
|
-
function prepareMonitoredItem(
|
|
517
|
+
function prepareMonitoredItem(_context: ISessionContext, addressSpace: AddressSpace, monitoredItem: MonitoredItem) {
|
|
512
518
|
const itemToMonitor = monitoredItem.itemToMonitor;
|
|
513
519
|
const readNodeFunc = build_scanning_node_function(addressSpace, itemToMonitor);
|
|
514
520
|
monitoredItem.samplingFunc = readNodeFunc;
|
|
@@ -523,7 +529,7 @@ function _installRegisterServerManager(self: OPCUAServer) {
|
|
|
523
529
|
assert(self instanceof OPCUAServer);
|
|
524
530
|
assert(!self.registerServerManager);
|
|
525
531
|
|
|
526
|
-
/*
|
|
532
|
+
/* c8 ignore next */
|
|
527
533
|
if (!self.registerServerMethod) {
|
|
528
534
|
throw new Error("Internal Error");
|
|
529
535
|
}
|
|
@@ -545,7 +551,7 @@ function _installRegisterServerManager(self: OPCUAServer) {
|
|
|
545
551
|
server: self
|
|
546
552
|
});
|
|
547
553
|
break;
|
|
548
|
-
/*
|
|
554
|
+
/* c8 ignore next */
|
|
549
555
|
default:
|
|
550
556
|
throw new Error("Invalid switch");
|
|
551
557
|
}
|
|
@@ -590,7 +596,7 @@ function _installRegisterServerManager(self: OPCUAServer) {
|
|
|
590
596
|
}
|
|
591
597
|
|
|
592
598
|
function validate_applicationUri(channel: ServerSecureChannelLayer, request: CreateSessionRequest): boolean {
|
|
593
|
-
const applicationUri = request.clientDescription.applicationUri
|
|
599
|
+
const applicationUri = request.clientDescription.applicationUri || "";
|
|
594
600
|
const clientCertificate = request.clientCertificate;
|
|
595
601
|
// if session is insecure there is no need to check certificate information
|
|
596
602
|
if (channel.securityMode === MessageSecurityMode.None) {
|
|
@@ -600,11 +606,11 @@ function validate_applicationUri(channel: ServerSecureChannelLayer, request: Cre
|
|
|
600
606
|
return true; // can't check
|
|
601
607
|
}
|
|
602
608
|
const e = exploreCertificate(clientCertificate);
|
|
603
|
-
const uniformResourceIdentifier = e.tbsCertificate.extensions
|
|
609
|
+
const uniformResourceIdentifier = e.tbsCertificate.extensions?.subjectAltName?.uniformResourceIdentifier ?? null;
|
|
604
610
|
const applicationUriFromCert =
|
|
605
611
|
uniformResourceIdentifier && uniformResourceIdentifier.length > 0 ? uniformResourceIdentifier[0] : null;
|
|
606
612
|
|
|
607
|
-
/*
|
|
613
|
+
/* c8 ignore next */
|
|
608
614
|
if (applicationUriFromCert !== applicationUri) {
|
|
609
615
|
errorLog("BadCertificateUriInvalid!");
|
|
610
616
|
errorLog("applicationUri = ", applicationUri);
|
|
@@ -623,7 +629,7 @@ function validate_security_endpoint(
|
|
|
623
629
|
endpoint?: EndpointDescription;
|
|
624
630
|
} {
|
|
625
631
|
debugLog("validate_security_endpoint = ", request.endpointUrl);
|
|
626
|
-
let endpoints = server.
|
|
632
|
+
let endpoints = server.findMatchingEndpoints(request.endpointUrl);
|
|
627
633
|
// endpointUrl String The network address that the Client used to access the Session Endpoint.
|
|
628
634
|
// The HostName portion of the URL should be one of the HostNames for the application that are
|
|
629
635
|
// specified in the Server’s ApplicationInstanceCertificate (see 7.2). The Server shall raise an
|
|
@@ -636,22 +642,22 @@ function validate_security_endpoint(
|
|
|
636
642
|
// sometime endpoints have a extra leading "/" that can be ignored
|
|
637
643
|
// don't be too harsh.
|
|
638
644
|
if (endpoints.length === 0 && request.endpointUrl?.endsWith("/")) {
|
|
639
|
-
endpoints = server.
|
|
645
|
+
endpoints = server.findMatchingEndpoints(request.endpointUrl.slice(0, -1));
|
|
640
646
|
}
|
|
641
647
|
|
|
642
648
|
if (endpoints.length === 0) {
|
|
643
649
|
// we have a UrlMismatch here
|
|
644
|
-
const ua_server = server.engine.addressSpace
|
|
650
|
+
const ua_server = server.engine.addressSpace?.rootFolder.objects.server;
|
|
645
651
|
if (!request.endpointUrl?.match(/localhost/i) || OPCUAServer.requestExactEndpointUrl) {
|
|
646
652
|
warningLog("Cannot find suitable endpoints in available endpoints. endpointUri =", request.endpointUrl);
|
|
647
653
|
}
|
|
648
|
-
ua_server
|
|
654
|
+
ua_server?.raiseEvent("AuditUrlMismatchEventType", {
|
|
649
655
|
endpointUrl: { dataType: DataType.String, value: request.endpointUrl }
|
|
650
656
|
});
|
|
651
657
|
if (OPCUAServer.requestExactEndpointUrl) {
|
|
652
658
|
return { errCode: StatusCodes.BadServiceUnsupported };
|
|
653
659
|
} else {
|
|
654
|
-
endpoints = server.
|
|
660
|
+
endpoints = server.findMatchingEndpoints(null);
|
|
655
661
|
}
|
|
656
662
|
}
|
|
657
663
|
// ignore restricted endpoints
|
|
@@ -665,7 +671,7 @@ function validate_security_endpoint(
|
|
|
665
671
|
return { errCode: StatusCodes.BadSecurityModeRejected };
|
|
666
672
|
}
|
|
667
673
|
const endpoints_matching_security_policy = endpoints_matching_security_mode.filter((e: EndpointDescription) => {
|
|
668
|
-
return e.securityPolicyUri === channel
|
|
674
|
+
return e.securityPolicyUri === channel?.securityPolicy;
|
|
669
675
|
});
|
|
670
676
|
|
|
671
677
|
if (endpoints_matching_security_policy.length === 0) {
|
|
@@ -674,7 +680,10 @@ function validate_security_endpoint(
|
|
|
674
680
|
if (endpoints_matching_security_policy.length !== 1) {
|
|
675
681
|
debugLog("endpoints_matching_security_policy= ", endpoints_matching_security_policy.length);
|
|
676
682
|
}
|
|
677
|
-
return {
|
|
683
|
+
return {
|
|
684
|
+
errCode: StatusCodes.Good,
|
|
685
|
+
endpoint: endpoints_matching_security_policy[0]
|
|
686
|
+
};
|
|
678
687
|
}
|
|
679
688
|
|
|
680
689
|
export function filterDiagnosticInfo(returnDiagnostics: number, response: CallResponse): void {
|
|
@@ -748,6 +757,21 @@ export interface OPCUAServerEndpointOptions {
|
|
|
748
757
|
/** alternate hostname or IP to use */
|
|
749
758
|
alternateHostname?: string | string[];
|
|
750
759
|
|
|
760
|
+
/**
|
|
761
|
+
* Additional endpoint URL(s) to advertise.
|
|
762
|
+
*
|
|
763
|
+
* Use when the server is behind Docker port-mapping, a reverse proxy,
|
|
764
|
+
* or a NAT gateway. Each URL is parsed to extract hostname and port.
|
|
765
|
+
* Each entry can be a plain URL string (inherits all security
|
|
766
|
+
* settings from the main endpoint) or an
|
|
767
|
+
* `AdvertisedEndpointConfig` object with per-URL overrides.
|
|
768
|
+
* The server still listens on `port`.
|
|
769
|
+
*
|
|
770
|
+
* @example "opc.tcp://localhost:48481"
|
|
771
|
+
* @example ["opc.tcp://localhost:48481", { url: "opc.tcp://public:4840", securityModes: [MessageSecurityMode.SignAndEncrypt] }]
|
|
772
|
+
*/
|
|
773
|
+
advertisedEndpoints?: AdvertisedEndpoint | AdvertisedEndpoint[];
|
|
774
|
+
|
|
751
775
|
/**
|
|
752
776
|
* true, if discovery service on secure channel shall be disabled
|
|
753
777
|
*/
|
|
@@ -925,39 +949,18 @@ export interface OPCUAServerOptions extends OPCUABaseServerOptions, OPCUAServerE
|
|
|
925
949
|
transportSettings?: IServerTransportSettings;
|
|
926
950
|
}
|
|
927
951
|
|
|
928
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
929
|
-
export interface OPCUAServer {
|
|
930
|
-
/**
|
|
931
|
-
*
|
|
932
|
-
*/
|
|
933
|
-
engine: ServerEngine;
|
|
934
|
-
/**
|
|
935
|
-
*
|
|
936
|
-
*/
|
|
937
|
-
registerServerMethod: RegisterServerMethod;
|
|
938
|
-
/**
|
|
939
|
-
*
|
|
940
|
-
*/
|
|
941
|
-
discoveryServerEndpointUrl: string;
|
|
942
|
-
/**
|
|
943
|
-
*
|
|
944
|
-
*/
|
|
945
|
-
registerServerManager?: IRegisterServerManager;
|
|
946
|
-
/**
|
|
947
|
-
*
|
|
948
|
-
*/
|
|
949
|
-
capabilitiesForMDNS: string[];
|
|
950
|
-
/**
|
|
951
|
-
*
|
|
952
|
-
*/
|
|
953
|
-
userCertificateManager: OPCUACertificateManager;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
952
|
const g_requestExactEndpointUrl = !!process.env.NODEOPCUA_SERVER_REQUEST_EXACT_ENDPOINT_URL;
|
|
957
953
|
/**
|
|
958
954
|
*
|
|
959
955
|
*/
|
|
960
956
|
export class OPCUAServer extends OPCUABaseServer {
|
|
957
|
+
public engine!: ServerEngine;
|
|
958
|
+
public registerServerMethod: RegisterServerMethod;
|
|
959
|
+
public discoveryServerEndpointUrl!: string;
|
|
960
|
+
public registerServerManager?: IRegisterServerManager;
|
|
961
|
+
public capabilitiesForMDNS: string[];
|
|
962
|
+
public userCertificateManager: OPCUACertificateManager;
|
|
963
|
+
|
|
961
964
|
static defaultShutdownTimeout = 100; // 250 ms
|
|
962
965
|
/**
|
|
963
966
|
* if requestExactEndpointUrl is set to true the server will only accept createSession that have a endpointUrl that strictly matches
|
|
@@ -1069,6 +1072,126 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1069
1072
|
return this.engine ? this.engine.isAuditing : false;
|
|
1070
1073
|
}
|
|
1071
1074
|
|
|
1075
|
+
/**
|
|
1076
|
+
* Set the current server state.
|
|
1077
|
+
*
|
|
1078
|
+
* Updates both the internal state and the
|
|
1079
|
+
* `Server.ServerStatus.State` variable in the
|
|
1080
|
+
* address space so that OPC UA reads reflect the
|
|
1081
|
+
* new state immediately.
|
|
1082
|
+
*/
|
|
1083
|
+
public setServerState(serverState: ServerState): void {
|
|
1084
|
+
this.engine.setServerState(serverState);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/**
|
|
1088
|
+
* Read the current `ServerState` from the
|
|
1089
|
+
* internal server status.
|
|
1090
|
+
*/
|
|
1091
|
+
public getServerState(): ServerState {
|
|
1092
|
+
return this.engine.getServerState();
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Set or clear a temporary role-policy override.
|
|
1097
|
+
*
|
|
1098
|
+
* When set, the override's `getUserRoles(username)`
|
|
1099
|
+
* is called **before** the default `userManager`.
|
|
1100
|
+
* Returning a `NodeId[]` overrides the roles;
|
|
1101
|
+
* returning `null` falls through to the default.
|
|
1102
|
+
*
|
|
1103
|
+
* Call with `null` to remove the override and
|
|
1104
|
+
* restore default behavior.
|
|
1105
|
+
*/
|
|
1106
|
+
public setRolePolicyOverride(override: IRolePolicyOverride | null): void {
|
|
1107
|
+
(this as unknown as IServerBase).rolePolicyOverride = override;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
/**
|
|
1111
|
+
* Set `ServerConfiguration.InApplicationSetup` in
|
|
1112
|
+
* the address space.
|
|
1113
|
+
*
|
|
1114
|
+
* Indicates whether the server is in its initial
|
|
1115
|
+
* application setup phase (e.g. awaiting GDS
|
|
1116
|
+
* provisioning).
|
|
1117
|
+
*/
|
|
1118
|
+
public setInApplicationSetup(value: boolean): void {
|
|
1119
|
+
this.engine.setInApplicationSetup(value);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Read the current value of
|
|
1124
|
+
* `ServerConfiguration.InApplicationSetup`.
|
|
1125
|
+
*/
|
|
1126
|
+
public getInApplicationSetup(): boolean {
|
|
1127
|
+
return this.engine.getInApplicationSetup();
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/**
|
|
1131
|
+
* Collect additional hostnames for the self-signed certificate SAN.
|
|
1132
|
+
*
|
|
1133
|
+
* Merges hostnames from `alternateHostname` and parsed
|
|
1134
|
+
* `advertisedEndpoints` URLs so the certificate covers all
|
|
1135
|
+
* configured addresses.
|
|
1136
|
+
*
|
|
1137
|
+
* IP literals (v4/v6) are **excluded** — they are handled by
|
|
1138
|
+
* `getConfiguredIPs()` and placed in the SAN `iPAddress` entries.
|
|
1139
|
+
*
|
|
1140
|
+
* @internal
|
|
1141
|
+
*/
|
|
1142
|
+
protected override getConfiguredHostnames(): string[] {
|
|
1143
|
+
return this._collectAlternateValues().hostnames;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/**
|
|
1147
|
+
* Collect additional IP addresses for the self-signed certificate SAN.
|
|
1148
|
+
*
|
|
1149
|
+
* Merges IP literals from `alternateHostname` and parsed
|
|
1150
|
+
* `advertisedEndpoints` URLs so the certificate covers all
|
|
1151
|
+
* configured IP addresses.
|
|
1152
|
+
*
|
|
1153
|
+
* @internal
|
|
1154
|
+
*/
|
|
1155
|
+
protected override getConfiguredIPs(): string[] {
|
|
1156
|
+
return this._collectAlternateValues().ips;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Classify all values from `alternateHostname` and
|
|
1161
|
+
* `advertisedEndpoints` into hostnames vs IP addresses using
|
|
1162
|
+
* `isIPAddress()` (wraps `net.isIP()`).
|
|
1163
|
+
*/
|
|
1164
|
+
private _collectAlternateValues(): { hostnames: string[]; ips: string[] } {
|
|
1165
|
+
const hostnames: string[] = [];
|
|
1166
|
+
const ips: string[] = [];
|
|
1167
|
+
|
|
1168
|
+
// alternateHostname
|
|
1169
|
+
const alt = this.options.alternateHostname;
|
|
1170
|
+
if (alt) {
|
|
1171
|
+
const altArray = Array.isArray(alt) ? alt : [alt];
|
|
1172
|
+
for (const value of altArray) {
|
|
1173
|
+
if (isIPAddress(value)) {
|
|
1174
|
+
ips.push(value);
|
|
1175
|
+
} else {
|
|
1176
|
+
hostnames.push(value);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// advertisedEndpoints — normalize to AdvertisedEndpointConfig[]
|
|
1182
|
+
const advList = normalizeAdvertisedEndpoints(this.options.advertisedEndpoints);
|
|
1183
|
+
for (const config of advList) {
|
|
1184
|
+
const { hostname } = parseOpcTcpUrl(config.url);
|
|
1185
|
+
if (isIPAddress(hostname)) {
|
|
1186
|
+
ips.push(hostname);
|
|
1187
|
+
} else {
|
|
1188
|
+
hostnames.push(hostname);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
return { hostnames, ips };
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1072
1195
|
public static registry = new ObjectRegistry();
|
|
1073
1196
|
public static fallbackSessionName = "Client didn't provide a meaningful sessionName ...";
|
|
1074
1197
|
/**
|
|
@@ -1167,7 +1290,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1167
1290
|
// note: we need to delay initialization of endpoint as certain resources
|
|
1168
1291
|
// such as %FQDN% might not be ready yet at this stage
|
|
1169
1292
|
this._delayInit = async () => {
|
|
1170
|
-
/*
|
|
1293
|
+
/* c8 ignore next */
|
|
1171
1294
|
if (!options) {
|
|
1172
1295
|
throw new Error("Internal Error");
|
|
1173
1296
|
}
|
|
@@ -1175,7 +1298,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1175
1298
|
|
|
1176
1299
|
// note: applicationUri is handled in a special way
|
|
1177
1300
|
this.engine = new ServerEngine({
|
|
1178
|
-
applicationUri: () => this.serverInfo.applicationUri
|
|
1301
|
+
applicationUri: () => this.serverInfo.applicationUri || "",
|
|
1179
1302
|
buildInfo,
|
|
1180
1303
|
isAuditing: options.isAuditing,
|
|
1181
1304
|
serverCapabilities: options.serverCapabilities,
|
|
@@ -1213,6 +1336,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1213
1336
|
host: options.host,
|
|
1214
1337
|
allowAnonymous: options.allowAnonymous,
|
|
1215
1338
|
alternateHostname: options.alternateHostname,
|
|
1339
|
+
advertisedEndpoints: options.advertisedEndpoints,
|
|
1216
1340
|
disableDiscovery: options.disableDiscovery,
|
|
1217
1341
|
securityModes: options.securityModes,
|
|
1218
1342
|
securityPolicies: options.securityPolicies
|
|
@@ -1220,7 +1344,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1220
1344
|
}
|
|
1221
1345
|
// todo should self.serverInfo.productUri match self.engine.buildInfo.productUri ?
|
|
1222
1346
|
for (const endpointOptions of endpointDefinitions) {
|
|
1223
|
-
const endPoint = this.createEndpointDescriptions(options
|
|
1347
|
+
const endPoint = this.createEndpointDescriptions(options, endpointOptions);
|
|
1224
1348
|
this.endpoints.push(endPoint);
|
|
1225
1349
|
endPoint.on("message", (message: Message, channel: ServerSecureChannelLayer) => {
|
|
1226
1350
|
this.on_request(message, channel);
|
|
@@ -1262,12 +1386,12 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1262
1386
|
*/
|
|
1263
1387
|
public initialize(): Promise<void>;
|
|
1264
1388
|
public initialize(done: () => void): void;
|
|
1265
|
-
public initialize(...args: [
|
|
1389
|
+
public initialize(...args: [((err?: Error) => void)?]): Promise<void> | void {
|
|
1266
1390
|
const done = args[0] as (err?: Error) => void;
|
|
1267
1391
|
assert(!this.initialized, "server is already initialized"); // already initialized ?
|
|
1268
1392
|
|
|
1269
1393
|
this._preInitTask.push(async () => {
|
|
1270
|
-
/*
|
|
1394
|
+
/* c8 ignore next */
|
|
1271
1395
|
if (this._delayInit) {
|
|
1272
1396
|
await this._delayInit();
|
|
1273
1397
|
this._delayInit = undefined;
|
|
@@ -1278,7 +1402,11 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1278
1402
|
.then(() => {
|
|
1279
1403
|
OPCUAServer.registry.register(this);
|
|
1280
1404
|
this.engine.initialize(this.options, () => {
|
|
1281
|
-
|
|
1405
|
+
if (!this.engine.addressSpace) {
|
|
1406
|
+
done(new Error("no addressSpace"));
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
bindRoleSet(this.userManager, this.engine.addressSpace);
|
|
1282
1410
|
setImmediate(() => {
|
|
1283
1411
|
this.emit("post_initialize");
|
|
1284
1412
|
done();
|
|
@@ -1295,38 +1423,36 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1295
1423
|
*/
|
|
1296
1424
|
public start(): Promise<void>;
|
|
1297
1425
|
public start(done: () => void): void;
|
|
1298
|
-
public start(...args: [
|
|
1299
|
-
const
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1426
|
+
public start(...args: [((err?: Error) => void)?]): Promise<void> | void {
|
|
1427
|
+
const callback = args[0];
|
|
1428
|
+
if (callback) {
|
|
1429
|
+
return super.start(callback);
|
|
1430
|
+
}
|
|
1431
|
+
return super.start();
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Initiate the server by starting all its endpoints
|
|
1435
|
+
* @private
|
|
1436
|
+
*/
|
|
1437
|
+
public async startAsync(): Promise<void> {
|
|
1438
|
+
await extractFullyQualifiedDomainName();
|
|
1303
1439
|
|
|
1304
1440
|
if (!this.initialized) {
|
|
1305
|
-
|
|
1306
|
-
this.initialize(callback);
|
|
1307
|
-
});
|
|
1441
|
+
await this.initialize();
|
|
1308
1442
|
}
|
|
1309
|
-
tasks.push((callback: ErrorCallback) => {
|
|
1310
|
-
super.start((err?: Error | null) => {
|
|
1311
|
-
if (err) {
|
|
1312
|
-
this.shutdown((/*err2*/ err2?: Error) => {
|
|
1313
|
-
callback(err);
|
|
1314
|
-
});
|
|
1315
|
-
} else {
|
|
1316
|
-
// we start the registration process asynchronously
|
|
1317
|
-
// as we want to make server immediately available
|
|
1318
|
-
this.registerServerManager!.start().then(() => {
|
|
1319
|
-
/* empty */
|
|
1320
|
-
}).catch((err) => {
|
|
1321
|
-
/* empty */
|
|
1322
|
-
});
|
|
1323
1443
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1444
|
+
try {
|
|
1445
|
+
await super.startAsync();
|
|
1446
|
+
} catch (err) {
|
|
1447
|
+
await this.shutdown();
|
|
1448
|
+
throw err;
|
|
1449
|
+
}
|
|
1328
1450
|
|
|
1329
|
-
|
|
1451
|
+
// we start the registration process asynchronously
|
|
1452
|
+
// as we want to make server immediately available
|
|
1453
|
+
this.registerServerManager?.start().catch(() => {
|
|
1454
|
+
/* empty */
|
|
1455
|
+
});
|
|
1330
1456
|
}
|
|
1331
1457
|
|
|
1332
1458
|
/**
|
|
@@ -1356,13 +1482,13 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1356
1482
|
public shutdown(timeout?: number): Promise<void>;
|
|
1357
1483
|
public shutdown(callback: (err?: Error) => void): void;
|
|
1358
1484
|
public shutdown(timeout: number, callback: (err?: Error) => void): void;
|
|
1359
|
-
public shutdown(...args: [
|
|
1485
|
+
public shutdown(...args: [number | ((err?: Error) => void) | undefined, ...((err?: Error) => void)[]]): Promise<void> | void {
|
|
1360
1486
|
const timeout = args.length === 1 ? OPCUAServer.defaultShutdownTimeout : (args[0] as number);
|
|
1361
1487
|
const callback = (args.length === 1 ? args[0] : args[1]) as (err?: Error) => void;
|
|
1362
1488
|
assert(typeof callback === "function");
|
|
1363
1489
|
debugLog("OPCUAServer#shutdown (timeout = ", timeout, ")");
|
|
1364
1490
|
|
|
1365
|
-
/*
|
|
1491
|
+
/* c8 ignore next */
|
|
1366
1492
|
if (!this.engine) {
|
|
1367
1493
|
return callback();
|
|
1368
1494
|
}
|
|
@@ -1380,28 +1506,31 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1380
1506
|
const shutdownTime = new Date(Date.now() + timeout);
|
|
1381
1507
|
this.engine.setShutdownTime(shutdownTime);
|
|
1382
1508
|
|
|
1383
|
-
debugLog("OPCUAServer is now un-registering itself from the discovery server "
|
|
1384
|
-
this.registerServerManager
|
|
1509
|
+
debugLog("OPCUAServer is now un-registering itself from the discovery server ", this.buildInfo);
|
|
1510
|
+
if (!this.registerServerManager) {
|
|
1511
|
+
callback(new Error("invalid register server manager"));
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
this.registerServerManager
|
|
1515
|
+
.stop()
|
|
1385
1516
|
.then(() => {
|
|
1386
1517
|
debugLog("OPCUAServer unregistered from discovery server successfully");
|
|
1387
|
-
|
|
1388
1518
|
})
|
|
1389
1519
|
.catch((err) => {
|
|
1390
1520
|
debugLog("OPCUAServer unregistered from discovery server with err: ", err.message);
|
|
1391
|
-
})
|
|
1392
|
-
|
|
1521
|
+
})
|
|
1522
|
+
.finally(() => {
|
|
1393
1523
|
setTimeout(async () => {
|
|
1394
1524
|
await this.engine.shutdown();
|
|
1395
1525
|
|
|
1396
1526
|
debugLog("OPCUAServer#shutdown: started");
|
|
1397
|
-
OPCUABaseServer.prototype.shutdown.call(this, (err1?: Error) => {
|
|
1527
|
+
OPCUABaseServer.prototype.shutdown.call(this, (err1?: Error | null) => {
|
|
1398
1528
|
debugLog("OPCUAServer#shutdown: completed");
|
|
1399
1529
|
|
|
1400
1530
|
this.dispose();
|
|
1401
|
-
callback(err1);
|
|
1531
|
+
callback(err1 || undefined);
|
|
1402
1532
|
});
|
|
1403
1533
|
}, timeout);
|
|
1404
|
-
|
|
1405
1534
|
});
|
|
1406
1535
|
}
|
|
1407
1536
|
|
|
@@ -1419,7 +1548,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1419
1548
|
}
|
|
1420
1549
|
OPCUAServer.registry.unregister(this);
|
|
1421
1550
|
|
|
1422
|
-
/*
|
|
1551
|
+
/* c8 ignore next */
|
|
1423
1552
|
if (this.engine) {
|
|
1424
1553
|
this.engine.dispose();
|
|
1425
1554
|
}
|
|
@@ -1439,7 +1568,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1439
1568
|
public raiseEvent(eventType: "AuditCertificateMismatchEventType", options: RaiseAuditCertificateMismatchEventData): void;
|
|
1440
1569
|
|
|
1441
1570
|
public raiseEvent(eventType: EventTypeLike | UAObjectType, options: RaiseEventData): void {
|
|
1442
|
-
/*
|
|
1571
|
+
/* c8 ignore next */
|
|
1443
1572
|
if (!this.engine.addressSpace) {
|
|
1444
1573
|
errorLog("addressSpace missing");
|
|
1445
1574
|
return;
|
|
@@ -1447,7 +1576,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1447
1576
|
|
|
1448
1577
|
const server = this.engine.addressSpace.findNode("Server") as UAObject;
|
|
1449
1578
|
|
|
1450
|
-
/*
|
|
1579
|
+
/* c8 ignore next */
|
|
1451
1580
|
if (!server) {
|
|
1452
1581
|
// xx throw new Error("OPCUAServer#raiseEvent : cannot find Server object");
|
|
1453
1582
|
return;
|
|
@@ -1457,12 +1586,12 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1457
1586
|
if (typeof eventType === "string") {
|
|
1458
1587
|
eventTypeNode = this.engine.addressSpace.findEventType(eventType);
|
|
1459
1588
|
if (eventTypeNode) {
|
|
1460
|
-
|
|
1589
|
+
server.raiseEvent(eventTypeNode, options);
|
|
1461
1590
|
} else {
|
|
1462
1591
|
console.warn(" cannot find event type ", eventType);
|
|
1463
1592
|
}
|
|
1464
1593
|
} else {
|
|
1465
|
-
|
|
1594
|
+
server.raiseEvent(eventTypeNode, options);
|
|
1466
1595
|
}
|
|
1467
1596
|
}
|
|
1468
1597
|
|
|
@@ -1471,7 +1600,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1471
1600
|
* @private
|
|
1472
1601
|
*/
|
|
1473
1602
|
protected createSession(options: CreateSessionOption): ServerSession {
|
|
1474
|
-
/*
|
|
1603
|
+
/* c8 ignore next */
|
|
1475
1604
|
if (!this.engine) {
|
|
1476
1605
|
throw new Error("Internal Error");
|
|
1477
1606
|
}
|
|
@@ -1512,47 +1641,51 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1512
1641
|
channel: ServerSecureChannelLayer,
|
|
1513
1642
|
clientSignature: SignatureData
|
|
1514
1643
|
): boolean {
|
|
1515
|
-
const clientCertificate = channel.clientCertificate
|
|
1644
|
+
const clientCertificate = channel.clientCertificate;
|
|
1516
1645
|
const securityPolicy = channel.securityPolicy;
|
|
1517
1646
|
const serverCertificate = this.getCertificate();
|
|
1518
|
-
const result = verifySignature(serverCertificate, session.nonce
|
|
1647
|
+
const result = verifySignature(serverCertificate, session.nonce, clientSignature, clientCertificate, securityPolicy);
|
|
1519
1648
|
return result;
|
|
1520
1649
|
}
|
|
1521
1650
|
|
|
1522
1651
|
protected isValidUserNameIdentityToken(
|
|
1523
1652
|
channel: ServerSecureChannelLayer,
|
|
1524
|
-
|
|
1653
|
+
_session: ServerSession,
|
|
1525
1654
|
userTokenPolicy: UserTokenPolicy,
|
|
1526
1655
|
userIdentityToken: UserNameIdentityToken,
|
|
1527
|
-
|
|
1656
|
+
_userTokenSignature: SignatureData,
|
|
1528
1657
|
callback: (err: Error | null, statusCode?: StatusCode) => void
|
|
1529
1658
|
): void {
|
|
1530
1659
|
assert(userIdentityToken instanceof UserNameIdentityToken);
|
|
1531
1660
|
|
|
1532
1661
|
const securityPolicy = adjustSecurityPolicy(channel, userTokenPolicy.securityPolicyUri);
|
|
1533
1662
|
if (securityPolicy === SecurityPolicy.None) {
|
|
1534
|
-
|
|
1663
|
+
callback(null, StatusCodes.Good);
|
|
1664
|
+
return;
|
|
1535
1665
|
}
|
|
1536
1666
|
const cryptoFactory = getCryptoFactory(securityPolicy);
|
|
1537
1667
|
|
|
1538
|
-
/*
|
|
1668
|
+
/* c8 ignore next */
|
|
1539
1669
|
if (!cryptoFactory) {
|
|
1540
|
-
|
|
1670
|
+
callback(null, StatusCodes.BadSecurityPolicyRejected);
|
|
1671
|
+
return;
|
|
1541
1672
|
}
|
|
1542
1673
|
|
|
1543
|
-
/*
|
|
1674
|
+
/* c8 ignore next */
|
|
1544
1675
|
if (userIdentityToken.encryptionAlgorithm !== cryptoFactory.asymmetricEncryptionAlgorithm) {
|
|
1545
1676
|
errorLog("invalid encryptionAlgorithm");
|
|
1546
1677
|
errorLog("userTokenPolicy", userTokenPolicy.toString());
|
|
1547
1678
|
errorLog("userTokenPolicy", userIdentityToken.toString());
|
|
1548
|
-
|
|
1679
|
+
callback(null, StatusCodes.BadIdentityTokenInvalid);
|
|
1680
|
+
return;
|
|
1549
1681
|
}
|
|
1550
1682
|
const userName = userIdentityToken.userName;
|
|
1551
1683
|
const password = userIdentityToken.password;
|
|
1552
1684
|
if (!userName || !password) {
|
|
1553
|
-
|
|
1685
|
+
callback(null, StatusCodes.BadIdentityTokenInvalid);
|
|
1686
|
+
return;
|
|
1554
1687
|
}
|
|
1555
|
-
|
|
1688
|
+
callback(null, StatusCodes.Good);
|
|
1556
1689
|
}
|
|
1557
1690
|
|
|
1558
1691
|
protected isValidX509IdentityToken(
|
|
@@ -1569,40 +1702,43 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1569
1702
|
const securityPolicy = adjustSecurityPolicy(channel, userTokenPolicy.securityPolicyUri);
|
|
1570
1703
|
|
|
1571
1704
|
const cryptoFactory = getCryptoFactory(securityPolicy);
|
|
1572
|
-
/*
|
|
1705
|
+
/* c8 ignore next */
|
|
1573
1706
|
if (!cryptoFactory) {
|
|
1574
|
-
|
|
1707
|
+
callback(null, StatusCodes.BadSecurityPolicyRejected);
|
|
1708
|
+
return;
|
|
1575
1709
|
}
|
|
1576
1710
|
|
|
1577
1711
|
if (!userTokenSignature || !userTokenSignature.signature) {
|
|
1578
1712
|
this.raiseEvent("AuditCreateSessionEventType", {});
|
|
1579
|
-
|
|
1580
|
-
return
|
|
1713
|
+
callback(null, StatusCodes.BadUserSignatureInvalid);
|
|
1714
|
+
return;
|
|
1581
1715
|
}
|
|
1582
1716
|
|
|
1583
1717
|
if (userIdentityToken.policyId !== userTokenPolicy.policyId) {
|
|
1584
1718
|
errorLog("invalid encryptionAlgorithm");
|
|
1585
1719
|
errorLog("userTokenPolicy", userTokenPolicy.toString());
|
|
1586
1720
|
errorLog("userTokenPolicy", userIdentityToken.toString());
|
|
1587
|
-
|
|
1721
|
+
callback(null, StatusCodes.BadSecurityPolicyRejected);
|
|
1722
|
+
return;
|
|
1588
1723
|
}
|
|
1589
1724
|
const certificate = userIdentityToken.certificateData; /* as Certificate*/
|
|
1590
|
-
const nonce = session.nonce
|
|
1725
|
+
const nonce = session.nonce;
|
|
1726
|
+
if (!nonce || nonce.length === 0) {
|
|
1727
|
+
callback(null, StatusCodes.BadNonceInvalid);
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1591
1730
|
const serverCertificate = this.getCertificate();
|
|
1592
|
-
|
|
1593
|
-
assert(serverCertificate instanceof Buffer);
|
|
1594
|
-
assert(certificate instanceof Buffer, "expecting certificate to be a Buffer");
|
|
1595
|
-
assert(nonce instanceof Buffer, "expecting nonce to be a Buffer");
|
|
1596
1731
|
assert(userTokenSignature.signature instanceof Buffer, "expecting userTokenSignature to be a Buffer");
|
|
1597
1732
|
|
|
1598
1733
|
// verify proof of possession by checking certificate signature & server nonce correctness
|
|
1599
1734
|
if (!verifySignature(serverCertificate, nonce, userTokenSignature, certificate, securityPolicy)) {
|
|
1600
|
-
|
|
1735
|
+
callback(null, StatusCodes.BadUserSignatureInvalid);
|
|
1736
|
+
return;
|
|
1601
1737
|
}
|
|
1602
1738
|
|
|
1603
1739
|
// verify if certificate is Valid
|
|
1604
|
-
this.userCertificateManager
|
|
1605
|
-
/*
|
|
1740
|
+
this.userCertificateManager.checkCertificate(certificate, (err, certificateStatus) => {
|
|
1741
|
+
/* c8 ignore next */
|
|
1606
1742
|
if (err) {
|
|
1607
1743
|
return callback(err);
|
|
1608
1744
|
}
|
|
@@ -1612,31 +1748,55 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1612
1748
|
break;
|
|
1613
1749
|
case StatusCodes.BadCertificateUntrusted:
|
|
1614
1750
|
this.raiseEvent("AuditCertificateUntrustedEventType", {
|
|
1615
|
-
certificate: {
|
|
1616
|
-
|
|
1751
|
+
certificate: {
|
|
1752
|
+
dataType: DataType.ByteString,
|
|
1753
|
+
value: certificate
|
|
1754
|
+
},
|
|
1755
|
+
sourceName: {
|
|
1756
|
+
dataType: DataType.String,
|
|
1757
|
+
value: "Security/Certificate"
|
|
1758
|
+
}
|
|
1617
1759
|
});
|
|
1618
1760
|
break;
|
|
1619
1761
|
case StatusCodes.BadCertificateTimeInvalid:
|
|
1620
1762
|
case StatusCodes.BadCertificateIssuerTimeInvalid:
|
|
1621
1763
|
this.raiseEvent("AuditCertificateExpiredEventType", {
|
|
1622
|
-
certificate: {
|
|
1623
|
-
|
|
1764
|
+
certificate: {
|
|
1765
|
+
dataType: DataType.ByteString,
|
|
1766
|
+
value: certificate
|
|
1767
|
+
},
|
|
1768
|
+
sourceName: {
|
|
1769
|
+
dataType: DataType.String,
|
|
1770
|
+
value: "Security/Certificate"
|
|
1771
|
+
}
|
|
1624
1772
|
});
|
|
1625
1773
|
break;
|
|
1626
1774
|
case StatusCodes.BadCertificateRevoked:
|
|
1627
1775
|
case StatusCodes.BadCertificateRevocationUnknown:
|
|
1628
1776
|
case StatusCodes.BadCertificateIssuerRevocationUnknown:
|
|
1629
1777
|
this.raiseEvent("AuditCertificateRevokedEventType", {
|
|
1630
|
-
certificate: {
|
|
1631
|
-
|
|
1778
|
+
certificate: {
|
|
1779
|
+
dataType: DataType.ByteString,
|
|
1780
|
+
value: certificate
|
|
1781
|
+
},
|
|
1782
|
+
sourceName: {
|
|
1783
|
+
dataType: DataType.String,
|
|
1784
|
+
value: "Security/Certificate"
|
|
1785
|
+
}
|
|
1632
1786
|
});
|
|
1633
1787
|
break;
|
|
1634
1788
|
case StatusCodes.BadCertificateIssuerUseNotAllowed:
|
|
1635
1789
|
case StatusCodes.BadCertificateUseNotAllowed:
|
|
1636
1790
|
case StatusCodes.BadSecurityChecksFailed:
|
|
1637
1791
|
this.raiseEvent("AuditCertificateMismatchEventType", {
|
|
1638
|
-
certificate: {
|
|
1639
|
-
|
|
1792
|
+
certificate: {
|
|
1793
|
+
dataType: DataType.ByteString,
|
|
1794
|
+
value: certificate
|
|
1795
|
+
},
|
|
1796
|
+
sourceName: {
|
|
1797
|
+
dataType: DataType.String,
|
|
1798
|
+
value: "Security/Certificate"
|
|
1799
|
+
}
|
|
1640
1800
|
});
|
|
1641
1801
|
break;
|
|
1642
1802
|
}
|
|
@@ -1688,8 +1848,12 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1688
1848
|
|
|
1689
1849
|
const securityPolicy = adjustSecurityPolicy(channel, userTokenPolicy.securityPolicyUri);
|
|
1690
1850
|
|
|
1691
|
-
const userName = userIdentityToken.userName
|
|
1692
|
-
|
|
1851
|
+
const userName = userIdentityToken.userName;
|
|
1852
|
+
if (!userName) {
|
|
1853
|
+
callback(new Error(" expecting a no null username"));
|
|
1854
|
+
return;
|
|
1855
|
+
}
|
|
1856
|
+
let password: ByteString | string = userIdentityToken.password;
|
|
1693
1857
|
|
|
1694
1858
|
// decrypt password if necessary
|
|
1695
1859
|
if (securityPolicy === SecurityPolicy.None) {
|
|
@@ -1698,20 +1862,24 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1698
1862
|
} else {
|
|
1699
1863
|
const serverPrivateKey = this.getPrivateKey();
|
|
1700
1864
|
|
|
1701
|
-
const serverNonce = session.nonce
|
|
1702
|
-
|
|
1865
|
+
const serverNonce = session.nonce;
|
|
1866
|
+
if (!serverNonce) {
|
|
1867
|
+
callback(new Error(" expecting a no null nonce"));
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1703
1870
|
|
|
1704
1871
|
const cryptoFactory = getCryptoFactory(securityPolicy);
|
|
1705
|
-
/*
|
|
1872
|
+
/* c8 ignore next */
|
|
1706
1873
|
if (!cryptoFactory) {
|
|
1707
|
-
|
|
1874
|
+
callback(new Error(" Unsupported security Policy"));
|
|
1875
|
+
return;
|
|
1708
1876
|
}
|
|
1709
1877
|
|
|
1710
1878
|
const buff = cryptoFactory.asymmetricDecrypt(password, serverPrivateKey);
|
|
1711
1879
|
|
|
1712
1880
|
// server certificate may be invalid and asymmetricDecrypt may fail
|
|
1713
1881
|
if (!buff || buff.length < 4) {
|
|
1714
|
-
|
|
1882
|
+
setImmediate(() => callback(null, false));
|
|
1715
1883
|
return;
|
|
1716
1884
|
}
|
|
1717
1885
|
|
|
@@ -1737,41 +1905,30 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1737
1905
|
callback: (err: Error | null, statusCode?: StatusCode) => void
|
|
1738
1906
|
): void {
|
|
1739
1907
|
assert(typeof callback === "function");
|
|
1740
|
-
/*
|
|
1908
|
+
/* c8 ignore next */
|
|
1741
1909
|
if (!userIdentityToken) {
|
|
1742
1910
|
throw new Error("Invalid token");
|
|
1743
1911
|
}
|
|
1744
1912
|
|
|
1745
1913
|
const userTokenType = getTokenType(userIdentityToken);
|
|
1746
1914
|
|
|
1747
|
-
const userTokenPolicy = findUserTokenByPolicy(endpointDescription, userTokenType, userIdentityToken.policyId
|
|
1915
|
+
const userTokenPolicy = findUserTokenByPolicy(endpointDescription, userTokenType, userIdentityToken.policyId);
|
|
1748
1916
|
if (!userTokenPolicy) {
|
|
1749
1917
|
// cannot find token with this policyId
|
|
1750
|
-
|
|
1918
|
+
callback(null, StatusCodes.BadIdentityTokenInvalid);
|
|
1919
|
+
return;
|
|
1751
1920
|
}
|
|
1752
1921
|
//
|
|
1753
1922
|
if (userIdentityToken instanceof UserNameIdentityToken) {
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
session,
|
|
1757
|
-
userTokenPolicy,
|
|
1758
|
-
userIdentityToken,
|
|
1759
|
-
userTokenSignature,
|
|
1760
|
-
callback
|
|
1761
|
-
);
|
|
1923
|
+
this.isValidUserNameIdentityToken(channel, session, userTokenPolicy, userIdentityToken, userTokenSignature, callback);
|
|
1924
|
+
return;
|
|
1762
1925
|
}
|
|
1763
1926
|
if (userIdentityToken instanceof X509IdentityToken) {
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
session,
|
|
1767
|
-
userTokenPolicy,
|
|
1768
|
-
userIdentityToken,
|
|
1769
|
-
userTokenSignature,
|
|
1770
|
-
callback
|
|
1771
|
-
);
|
|
1927
|
+
this.isValidX509IdentityToken(channel, session, userTokenPolicy, userIdentityToken, userTokenSignature, callback);
|
|
1928
|
+
return;
|
|
1772
1929
|
}
|
|
1773
1930
|
|
|
1774
|
-
|
|
1931
|
+
callback(null, StatusCodes.Good);
|
|
1775
1932
|
}
|
|
1776
1933
|
|
|
1777
1934
|
/**
|
|
@@ -1793,16 +1950,18 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1793
1950
|
assert(typeof callback === "function");
|
|
1794
1951
|
|
|
1795
1952
|
const userTokenType = getTokenType(userIdentityToken);
|
|
1796
|
-
const userTokenPolicy = findUserTokenByPolicy(session.getEndpointDescription(), userTokenType, userIdentityToken.policyId
|
|
1797
|
-
/**
|
|
1953
|
+
const userTokenPolicy = findUserTokenByPolicy(session.getEndpointDescription(), userTokenType, userIdentityToken.policyId);
|
|
1954
|
+
/** c8 ignore next */
|
|
1798
1955
|
if (!userTokenPolicy) {
|
|
1799
|
-
|
|
1956
|
+
callback(null, false);
|
|
1957
|
+
return;
|
|
1800
1958
|
}
|
|
1801
1959
|
// find if a userToken exists
|
|
1802
1960
|
if (userIdentityToken instanceof UserNameIdentityToken) {
|
|
1803
|
-
|
|
1961
|
+
this.userNameIdentityTokenAuthenticateUser(channel, session, userTokenPolicy, userIdentityToken, callback);
|
|
1962
|
+
return;
|
|
1804
1963
|
}
|
|
1805
|
-
|
|
1964
|
+
setImmediate(callback.bind(null, null, true));
|
|
1806
1965
|
}
|
|
1807
1966
|
|
|
1808
1967
|
protected makeServerNonce(): Nonce {
|
|
@@ -1870,17 +2029,14 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1870
2029
|
if (channel.securityMode !== MessageSecurityMode.None) {
|
|
1871
2030
|
errorLog(
|
|
1872
2031
|
chalk.red("SERVER with secure connection: Missing or invalid client Nonce "),
|
|
1873
|
-
request.clientNonce
|
|
2032
|
+
request.clientNonce?.toString("hex")
|
|
1874
2033
|
);
|
|
1875
2034
|
|
|
1876
2035
|
return rejectConnection(this, StatusCodes.BadNonceInvalid);
|
|
1877
2036
|
}
|
|
1878
2037
|
}
|
|
1879
2038
|
if (nonceAlreadyBeenUsed(request.clientNonce)) {
|
|
1880
|
-
errorLog(
|
|
1881
|
-
chalk.red("SERVER with secure connection: Nonce has already been used"),
|
|
1882
|
-
request.clientNonce && request.clientNonce.toString("hex")
|
|
1883
|
-
);
|
|
2039
|
+
errorLog(chalk.red("SERVER with secure connection: Nonce has already been used"), request.clientNonce?.toString("hex"));
|
|
1884
2040
|
|
|
1885
2041
|
return rejectConnection(this, StatusCodes.BadNonceInvalid);
|
|
1886
2042
|
}
|
|
@@ -1932,7 +2088,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1932
2088
|
|
|
1933
2089
|
const hasEncryption = true;
|
|
1934
2090
|
// If the securityPolicyUri is None and none of the UserTokenPolicies requires encryption
|
|
1935
|
-
if (session.channel
|
|
2091
|
+
if (session.channel?.securityMode === MessageSecurityMode.None) {
|
|
1936
2092
|
// ToDo: Check that none of our insecure endpoint has a a UserTokenPolicy that require encryption
|
|
1937
2093
|
// and set hasEncryption = false under this condition
|
|
1938
2094
|
}
|
|
@@ -1969,7 +2125,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1969
2125
|
// securityPolicyUri, userIdentityTokens, transportProfileUri and securityLevel with all
|
|
1970
2126
|
// other parameters set to null. Only the recommended parameters shall be verified by
|
|
1971
2127
|
// the client.
|
|
1972
|
-
serverEndpoints: _serverEndpointsForCreateSessionResponse(this, session.endpoint
|
|
2128
|
+
serverEndpoints: _serverEndpointsForCreateSessionResponse(this, session.endpoint?.endpointUrl || "", request.serverUri),
|
|
1973
2129
|
|
|
1974
2130
|
// This parameter is deprecated and the array shall be empty.
|
|
1975
2131
|
serverSoftwareCertificates: null,
|
|
@@ -1995,7 +2151,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1995
2151
|
assert(typeof reason === "string");
|
|
1996
2152
|
if (this.isAuditing) {
|
|
1997
2153
|
assert(reason === "Timeout" || reason === "Terminated" || reason === "CloseSession" || reason === "Forcing");
|
|
1998
|
-
const sourceName =
|
|
2154
|
+
const sourceName = `Session/${reason}`;
|
|
1999
2155
|
|
|
2000
2156
|
this.raiseEvent("AuditSessionEventType", {
|
|
2001
2157
|
/* part 5 - 6.4.3 AuditEventType */
|
|
@@ -2047,18 +2203,27 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2047
2203
|
// identifier in all AuditEvents related to the Session Service Set (AuditCreateSessionEventType,
|
|
2048
2204
|
// AuditActivateSessionEventType and their subtypes) and the SecureChannel Service Set
|
|
2049
2205
|
// (AuditChannelEventType and its subtypes
|
|
2050
|
-
secureChannelId: {
|
|
2206
|
+
secureChannelId: {
|
|
2207
|
+
dataType: "String",
|
|
2208
|
+
value: session.channel?.channelId?.toString() ?? ""
|
|
2209
|
+
},
|
|
2051
2210
|
|
|
2052
2211
|
// Duration
|
|
2053
|
-
revisedSessionTimeout: {
|
|
2212
|
+
revisedSessionTimeout: {
|
|
2213
|
+
dataType: "Duration",
|
|
2214
|
+
value: session.sessionTimeout
|
|
2215
|
+
},
|
|
2054
2216
|
|
|
2055
2217
|
// clientCertificate
|
|
2056
|
-
clientCertificate: {
|
|
2218
|
+
clientCertificate: {
|
|
2219
|
+
dataType: "ByteString",
|
|
2220
|
+
value: session.channel?.clientCertificate ?? null
|
|
2221
|
+
},
|
|
2057
2222
|
|
|
2058
2223
|
// clientCertificateThumbprint
|
|
2059
2224
|
clientCertificateThumbprint: {
|
|
2060
2225
|
dataType: "String",
|
|
2061
|
-
value: thumbprint(session.channel
|
|
2226
|
+
value: thumbprint(session.channel?.clientCertificate)
|
|
2062
2227
|
}
|
|
2063
2228
|
});
|
|
2064
2229
|
}
|
|
@@ -2101,20 +2266,21 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2101
2266
|
server.engine.incrementSecurityRejectedSessionCount();
|
|
2102
2267
|
}
|
|
2103
2268
|
|
|
2104
|
-
const response1 = new ActivateSessionResponse({
|
|
2269
|
+
const response1 = new ActivateSessionResponse({
|
|
2270
|
+
responseHeader: { serviceResult: statusCode }
|
|
2271
|
+
});
|
|
2105
2272
|
|
|
2106
2273
|
channel.send_response("MSG", response1, message);
|
|
2107
2274
|
}
|
|
2108
2275
|
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
/* istanbul ignore next */
|
|
2276
|
+
/* c8 ignore next */
|
|
2112
2277
|
if (!session) {
|
|
2113
2278
|
// this may happen when the server has been restarted and a client tries to reconnect, thinking
|
|
2114
2279
|
// that the previous session may still be active
|
|
2115
2280
|
debugLog(chalk.yellow.bold(" Bad Session in _on_ActivateSessionRequest"), authenticationToken.toString());
|
|
2116
2281
|
|
|
2117
|
-
|
|
2282
|
+
rejectConnection(this, StatusCodes.BadSessionIdInvalid);
|
|
2283
|
+
return;
|
|
2118
2284
|
}
|
|
2119
2285
|
|
|
2120
2286
|
// tslint:disable-next-line: no-unused-expression
|
|
@@ -2128,8 +2294,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2128
2294
|
if (!channel_has_session(channel, session)) {
|
|
2129
2295
|
// it looks like session activation is being using a channel that is not the
|
|
2130
2296
|
// one that have been used to create the session
|
|
2131
|
-
errorLog(
|
|
2132
|
-
|
|
2297
|
+
errorLog(` channel.sessionTokens === ${Object.keys(channel.sessionTokens).join(" ")}`);
|
|
2298
|
+
rejectConnection(this, StatusCodes.BadSessionNotActivated);
|
|
2299
|
+
return;
|
|
2133
2300
|
}
|
|
2134
2301
|
}
|
|
2135
2302
|
|
|
@@ -2139,47 +2306,58 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2139
2306
|
// SecureChannel is the same as the Certificate used to create the original SecureChannel.
|
|
2140
2307
|
|
|
2141
2308
|
if (session.status === "active") {
|
|
2142
|
-
if (session.channel
|
|
2309
|
+
if (session.channel?.channelId !== channel.channelId) {
|
|
2143
2310
|
warningLog(
|
|
2144
2311
|
" Session ",
|
|
2145
2312
|
session.sessionName,
|
|
2146
2313
|
" is being transferred from channel",
|
|
2147
|
-
chalk.cyan(session.channel
|
|
2314
|
+
chalk.cyan(session.channel?.channelId?.toString()),
|
|
2148
2315
|
" to channel ",
|
|
2149
|
-
chalk.cyan(channel.channelId
|
|
2316
|
+
chalk.cyan(channel.channelId?.toString())
|
|
2150
2317
|
);
|
|
2151
2318
|
|
|
2152
2319
|
// session is being reassigned to a new Channel,
|
|
2153
2320
|
// we shall verify that the certificate used to create the Session is the same as the current
|
|
2154
2321
|
// channel certificate.
|
|
2155
|
-
const old_channel_cert_thumbprint = thumbprint(session.channel
|
|
2156
|
-
const new_channel_cert_thumbprint = thumbprint(channel.clientCertificate
|
|
2322
|
+
const old_channel_cert_thumbprint = thumbprint(session.channel?.clientCertificate);
|
|
2323
|
+
const new_channel_cert_thumbprint = thumbprint(channel.clientCertificate);
|
|
2157
2324
|
|
|
2158
2325
|
if (old_channel_cert_thumbprint !== new_channel_cert_thumbprint) {
|
|
2159
|
-
|
|
2326
|
+
rejectConnection(this, StatusCodes.BadNoValidCertificates); // not sure about this code !
|
|
2327
|
+
return;
|
|
2160
2328
|
}
|
|
2161
2329
|
|
|
2162
2330
|
// ... In addition the Server shall verify that the Client supplied a UserIdentityToken that is
|
|
2163
2331
|
// identical to the token currently associated with the Session reassign session to new channel.
|
|
2164
|
-
if (!sameIdentityToken(session.userIdentityToken
|
|
2165
|
-
|
|
2332
|
+
if (!sameIdentityToken(session.userIdentityToken, request.userIdentityToken as UserIdentityToken)) {
|
|
2333
|
+
rejectConnection(this, StatusCodes.BadIdentityChangeNotSupported); // not sure about this code !
|
|
2334
|
+
return;
|
|
2166
2335
|
}
|
|
2167
2336
|
}
|
|
2168
2337
|
} else if (session.status === "screwed") {
|
|
2169
2338
|
// session has been used before being activated => this should be detected and session should be dismissed.
|
|
2170
|
-
|
|
2339
|
+
rejectConnection(this, StatusCodes.BadSessionClosed);
|
|
2340
|
+
return;
|
|
2171
2341
|
} else if (session.status === "closed") {
|
|
2172
2342
|
warningLog(chalk.yellow.bold(" Bad Session Closed in _on_ActivateSessionRequest"), authenticationToken.toString());
|
|
2173
|
-
|
|
2343
|
+
rejectConnection(this, StatusCodes.BadSessionClosed);
|
|
2344
|
+
return;
|
|
2174
2345
|
}
|
|
2175
2346
|
|
|
2176
2347
|
// verify clientSignature provided by the client
|
|
2177
2348
|
if (!this.verifyClientSignature(session, channel, request.clientSignature)) {
|
|
2178
|
-
|
|
2349
|
+
rejectConnection(this, StatusCodes.BadApplicationSignatureInvalid);
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
const endpoint = session.endpoint;
|
|
2354
|
+
if (!endpoint) {
|
|
2355
|
+
rejectConnection(this, StatusCodes.BadSessionIdInvalid);
|
|
2356
|
+
return;
|
|
2179
2357
|
}
|
|
2180
2358
|
|
|
2181
2359
|
// userIdentityToken may be missing , assume anonymous access then
|
|
2182
|
-
request.userIdentityToken = request.userIdentityToken || createAnonymousIdentityToken(
|
|
2360
|
+
request.userIdentityToken = request.userIdentityToken || createAnonymousIdentityToken(endpoint);
|
|
2183
2361
|
|
|
2184
2362
|
// check request.userIdentityToken is correct ( expected type and correctly formed)
|
|
2185
2363
|
this.isValidUserIdentityToken(
|
|
@@ -2187,10 +2365,10 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2187
2365
|
session,
|
|
2188
2366
|
request.userIdentityToken as UserIdentityToken,
|
|
2189
2367
|
request.userTokenSignature,
|
|
2190
|
-
|
|
2191
|
-
(
|
|
2368
|
+
endpoint,
|
|
2369
|
+
(_err: Error | null, statusCode?: StatusCode) => {
|
|
2192
2370
|
if (!statusCode || statusCode.isNotGood()) {
|
|
2193
|
-
/*
|
|
2371
|
+
/* c8 ignore next */
|
|
2194
2372
|
if (!(statusCode && statusCode instanceof StatusCode)) {
|
|
2195
2373
|
return rejectConnection(this, StatusCodes.BadCertificateInvalid);
|
|
2196
2374
|
}
|
|
@@ -2203,7 +2381,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2203
2381
|
session,
|
|
2204
2382
|
request.userIdentityToken as UserIdentityToken,
|
|
2205
2383
|
(err1: Error | null, authorized?: boolean) => {
|
|
2206
|
-
/*
|
|
2384
|
+
/* c8 ignore next */
|
|
2207
2385
|
if (err1) {
|
|
2208
2386
|
return rejectConnection(this, StatusCodes.BadInternalError);
|
|
2209
2387
|
}
|
|
@@ -2223,7 +2401,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2223
2401
|
|
|
2224
2402
|
session.status = "active";
|
|
2225
2403
|
|
|
2226
|
-
response = new ActivateSessionResponse({
|
|
2404
|
+
const response = new ActivateSessionResponse({
|
|
2405
|
+
serverNonce: session.nonce
|
|
2406
|
+
});
|
|
2227
2407
|
channel.send_response("MSG", response, message);
|
|
2228
2408
|
|
|
2229
2409
|
// send OPCUA Event Notification
|
|
@@ -2261,13 +2441,18 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2261
2441
|
// --- check that provided session matches session attached to channel
|
|
2262
2442
|
if (channel.channelId !== session.channelId) {
|
|
2263
2443
|
if (!(request instanceof ActivateSessionRequest)) {
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2444
|
+
// Note: PublishRequests arriving on the new channel before
|
|
2445
|
+
// ActivateSession completes the session transfer are expected
|
|
2446
|
+
// transient occurrences, not errors worth logging.
|
|
2447
|
+
if (request.constructor.name !== "PublishRequest") {
|
|
2448
|
+
errorLog(
|
|
2449
|
+
chalk.red.bgWhite(
|
|
2450
|
+
`ERROR: channel.channelId !== session.channelId on processing request ${request.constructor.name}`
|
|
2451
|
+
),
|
|
2452
|
+
channel.channelId,
|
|
2453
|
+
session.channelId
|
|
2454
|
+
);
|
|
2455
|
+
}
|
|
2271
2456
|
}
|
|
2272
2457
|
message.session_statusCode = StatusCodes.BadSecureChannelIdInvalid;
|
|
2273
2458
|
} else if (channel_has_session(channel, session)) {
|
|
@@ -2315,9 +2500,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2315
2500
|
return channel.send_response("MSG", response1, message);
|
|
2316
2501
|
} catch (err) {
|
|
2317
2502
|
warningLog(err);
|
|
2318
|
-
//
|
|
2503
|
+
// c8 ignore next
|
|
2319
2504
|
if (types.isNativeError(err)) {
|
|
2320
|
-
//
|
|
2505
|
+
// c8 ignore next
|
|
2321
2506
|
errorLog(
|
|
2322
2507
|
"Internal error in issuing response\nplease contact support@sterfive.com",
|
|
2323
2508
|
message.request.toString(),
|
|
@@ -2325,7 +2510,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2325
2510
|
response1.toString()
|
|
2326
2511
|
);
|
|
2327
2512
|
}
|
|
2328
|
-
//
|
|
2513
|
+
// c8 ignore next
|
|
2329
2514
|
throw err;
|
|
2330
2515
|
}
|
|
2331
2516
|
}
|
|
@@ -2337,12 +2522,14 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2337
2522
|
return g_sendError(channel, message, ResponseClass, statusCode);
|
|
2338
2523
|
}
|
|
2339
2524
|
|
|
2340
|
-
|
|
2341
|
-
/* istanbul ignore next */
|
|
2525
|
+
/* c8 ignore next */
|
|
2342
2526
|
if (!message.session || message.session_statusCode !== StatusCodes.Good) {
|
|
2343
|
-
const
|
|
2344
|
-
|
|
2345
|
-
|
|
2527
|
+
const sessionStatusCode = message.session_statusCode ?? StatusCodes.BadInternalError;
|
|
2528
|
+
const errMessage = `=>${sessionStatusCode.toString()}`;
|
|
2529
|
+
const response = new ServiceFault({
|
|
2530
|
+
responseHeader: { serviceResult: sessionStatusCode }
|
|
2531
|
+
});
|
|
2532
|
+
debugLog(chalk.red.bold(errMessage), chalk.yellow(sessionStatusCode.toString()), response.constructor.name);
|
|
2346
2533
|
return sendResponse(response);
|
|
2347
2534
|
}
|
|
2348
2535
|
|
|
@@ -2398,7 +2585,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2398
2585
|
): Promise<void> {
|
|
2399
2586
|
assert(typeof actionToPerform === "function");
|
|
2400
2587
|
const request = message.request as unknown as { subscriptionId: number };
|
|
2401
|
-
assert(Object.
|
|
2588
|
+
assert(Object.hasOwn(request, "subscriptionId"));
|
|
2402
2589
|
|
|
2403
2590
|
this._apply_on_SessionObject(
|
|
2404
2591
|
ResponseClass,
|
|
@@ -2427,7 +2614,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2427
2614
|
): void {
|
|
2428
2615
|
assert(typeof actionToPerform === "function");
|
|
2429
2616
|
const request = message.request as unknown as { subscriptionIds: number[] };
|
|
2430
|
-
assert(Object.
|
|
2617
|
+
assert(Object.hasOwn(request, "subscriptionIds"));
|
|
2431
2618
|
|
|
2432
2619
|
this._apply_on_SessionObject(
|
|
2433
2620
|
ResponseClass,
|
|
@@ -2465,7 +2652,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2465
2652
|
responseHeader: {
|
|
2466
2653
|
serviceResult
|
|
2467
2654
|
},
|
|
2468
|
-
results: results as
|
|
2655
|
+
results: results as unknown as StatusCode[]
|
|
2469
2656
|
});
|
|
2470
2657
|
sendResponse(response);
|
|
2471
2658
|
}
|
|
@@ -2483,7 +2670,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2483
2670
|
message,
|
|
2484
2671
|
channel,
|
|
2485
2672
|
async (session: ServerSession, subscriptionId: number) => {
|
|
2486
|
-
/*
|
|
2673
|
+
/* c8 ignore next */
|
|
2487
2674
|
if (isSubscriptionIdInvalid(subscriptionId)) {
|
|
2488
2675
|
return StatusCodes.BadSubscriptionIdInvalid;
|
|
2489
2676
|
}
|
|
@@ -2502,7 +2689,10 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2502
2689
|
if (session) {
|
|
2503
2690
|
const subscriptions = session.publishEngine.subscriptions;
|
|
2504
2691
|
for (const subscription of subscriptions) {
|
|
2505
|
-
|
|
2692
|
+
const onDeleteFn = this.options.onDeleteMonitoredItem;
|
|
2693
|
+
await subscription.applyOnMonitoredItem(async (monitoredItem: MonitoredItem) => {
|
|
2694
|
+
await onDeleteFn(subscription, monitoredItem);
|
|
2695
|
+
});
|
|
2506
2696
|
}
|
|
2507
2697
|
}
|
|
2508
2698
|
}
|
|
@@ -2517,8 +2707,6 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2517
2707
|
const request = message.request as CloseSessionRequest;
|
|
2518
2708
|
assert(request instanceof CloseSessionRequest);
|
|
2519
2709
|
|
|
2520
|
-
let response;
|
|
2521
|
-
|
|
2522
2710
|
message.session_statusCode = StatusCodes.Good;
|
|
2523
2711
|
|
|
2524
2712
|
function sendError(statusCode: StatusCode) {
|
|
@@ -2535,11 +2723,12 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2535
2723
|
|
|
2536
2724
|
const session = message.session;
|
|
2537
2725
|
if (!session) {
|
|
2538
|
-
|
|
2726
|
+
sendError(StatusCodes.BadSessionIdInvalid);
|
|
2727
|
+
return;
|
|
2539
2728
|
}
|
|
2540
2729
|
|
|
2541
2730
|
// session has been created but not activated !
|
|
2542
|
-
const
|
|
2731
|
+
const _wasNotActivated = session.status === "new";
|
|
2543
2732
|
|
|
2544
2733
|
(async () => {
|
|
2545
2734
|
try {
|
|
@@ -2549,9 +2738,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2549
2738
|
// return sendError(StatusCodes.BadSessionNotActivated);
|
|
2550
2739
|
// }
|
|
2551
2740
|
|
|
2552
|
-
response = new CloseSessionResponse({});
|
|
2741
|
+
const response = new CloseSessionResponse({});
|
|
2553
2742
|
sendResponse(response);
|
|
2554
|
-
} catch (
|
|
2743
|
+
} catch (_err) {
|
|
2555
2744
|
sendError(StatusCodes.BadInternalError);
|
|
2556
2745
|
}
|
|
2557
2746
|
})();
|
|
@@ -2566,7 +2755,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2566
2755
|
protected _on_BrowseRequest(message: Message, channel: ServerSecureChannelLayer): void {
|
|
2567
2756
|
const request = message.request as BrowseRequest;
|
|
2568
2757
|
assert(request instanceof BrowseRequest);
|
|
2569
|
-
const diagnostic:
|
|
2758
|
+
const diagnostic: Record<string, unknown> = {};
|
|
2570
2759
|
|
|
2571
2760
|
this._apply_on_SessionObject(
|
|
2572
2761
|
BrowseResponse,
|
|
@@ -2576,7 +2765,11 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2576
2765
|
let response: BrowseResponse;
|
|
2577
2766
|
// test view
|
|
2578
2767
|
if (request.view && !request.view.viewId.isEmpty()) {
|
|
2579
|
-
|
|
2768
|
+
const addressSpace = this.engine.addressSpace;
|
|
2769
|
+
if (!addressSpace) {
|
|
2770
|
+
return sendError(StatusCodes.BadInternalError);
|
|
2771
|
+
}
|
|
2772
|
+
let theView: UAView | null = addressSpace.findNode(request.view.viewId) as UAView;
|
|
2580
2773
|
if (theView && theView.nodeClass !== NodeClass.View) {
|
|
2581
2774
|
// Error: theView is not a View
|
|
2582
2775
|
diagnostic.localizedText = { text: "Expecting a view here" };
|
|
@@ -2602,9 +2795,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2602
2795
|
assert(request.nodesToBrowse[0].schema.name === "BrowseDescription");
|
|
2603
2796
|
|
|
2604
2797
|
const context = session.sessionContext;
|
|
2605
|
-
const browseAll = (
|
|
2798
|
+
const browseAll = (_nodesToBrowse: BrowseDescriptionOptions[], callack: CallbackT<BrowseResult[]>) => {
|
|
2606
2799
|
const f = callbackify(this.engine.browseWithAutomaticExpansion).bind(this.engine);
|
|
2607
|
-
|
|
2800
|
+
f(request.nodesToBrowse ?? [], context, callack);
|
|
2608
2801
|
};
|
|
2609
2802
|
// handle continuation point and requestedMaxReferencesPerNode
|
|
2610
2803
|
const maxBrowseContinuationPoints = this.engine.serverCapabilities.maxBrowseContinuationPoints;
|
|
@@ -2618,7 +2811,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2618
2811
|
maxBrowseContinuationPoints
|
|
2619
2812
|
},
|
|
2620
2813
|
request.nodesToBrowse,
|
|
2621
|
-
(
|
|
2814
|
+
(_err, results) => {
|
|
2622
2815
|
if (!results) {
|
|
2623
2816
|
return sendError(StatusCodes.BadInternalError);
|
|
2624
2817
|
}
|
|
@@ -2711,10 +2904,10 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2711
2904
|
}
|
|
2712
2905
|
|
|
2713
2906
|
// ask for a refresh of asynchronous variables
|
|
2714
|
-
this.engine.refreshValues(request.nodesToRead, request.maxAge, (
|
|
2907
|
+
this.engine.refreshValues(request.nodesToRead, request.maxAge, (_err?: Error | null) => {
|
|
2715
2908
|
this.engine.read(context, request).then((results) => {
|
|
2716
2909
|
assert(results[0].schema.name === "DataValue");
|
|
2717
|
-
assert(results.length === request.nodesToRead
|
|
2910
|
+
assert(results.length === request.nodesToRead?.length);
|
|
2718
2911
|
|
|
2719
2912
|
const response = new ReadResponse({
|
|
2720
2913
|
diagnosticInfos: undefined,
|
|
@@ -2722,7 +2915,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2722
2915
|
});
|
|
2723
2916
|
// set it here for performance
|
|
2724
2917
|
response.results = results;
|
|
2725
|
-
assert(response.diagnosticInfos
|
|
2918
|
+
assert(response.diagnosticInfos?.length === 0);
|
|
2726
2919
|
sendResponse(response);
|
|
2727
2920
|
});
|
|
2728
2921
|
});
|
|
@@ -2741,8 +2934,6 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2741
2934
|
message,
|
|
2742
2935
|
channel,
|
|
2743
2936
|
(session: ServerSession, sendResponse: (response: Response) => void, sendError: (statsCode: StatusCode) => void) => {
|
|
2744
|
-
let response;
|
|
2745
|
-
|
|
2746
2937
|
const timestampsToReturn = request.timestampsToReturn;
|
|
2747
2938
|
|
|
2748
2939
|
if (timestampsToReturn === TimestampsToReturn.Invalid) {
|
|
@@ -2785,17 +2976,17 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2785
2976
|
.historyRead(context, request)
|
|
2786
2977
|
.then((results: HistoryReadResult[]) => {
|
|
2787
2978
|
assert(results[0].schema.name === "HistoryReadResult");
|
|
2788
|
-
assert(results.length === request.nodesToRead
|
|
2979
|
+
assert(results.length === request.nodesToRead?.length);
|
|
2789
2980
|
|
|
2790
|
-
response = new HistoryReadResponse({
|
|
2981
|
+
const response = new HistoryReadResponse({
|
|
2791
2982
|
diagnosticInfos: undefined,
|
|
2792
2983
|
results
|
|
2793
2984
|
});
|
|
2794
2985
|
|
|
2795
|
-
assert(response.diagnosticInfos
|
|
2986
|
+
assert(response.diagnosticInfos?.length === 0);
|
|
2796
2987
|
sendResponse(response);
|
|
2797
2988
|
})
|
|
2798
|
-
.catch((
|
|
2989
|
+
.catch((_err) => {
|
|
2799
2990
|
return sendError(StatusCodes.BadHistoryOperationInvalid);
|
|
2800
2991
|
});
|
|
2801
2992
|
});
|
|
@@ -2827,8 +3018,6 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2827
3018
|
message,
|
|
2828
3019
|
channel,
|
|
2829
3020
|
(session: ServerSession, sendResponse: (response: Response) => void, sendError: (statusCode: StatusCode) => void) => {
|
|
2830
|
-
let response;
|
|
2831
|
-
|
|
2832
3021
|
if (!request.nodesToWrite || request.nodesToWrite.length === 0) {
|
|
2833
3022
|
return sendError(StatusCodes.BadNothingToDo);
|
|
2834
3023
|
}
|
|
@@ -2851,8 +3040,8 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2851
3040
|
this.engine
|
|
2852
3041
|
.write(context, request.nodesToWrite)
|
|
2853
3042
|
.then((results: StatusCode[]) => {
|
|
2854
|
-
assert(results
|
|
2855
|
-
response = new WriteResponse({
|
|
3043
|
+
assert(results?.length === request.nodesToWrite?.length);
|
|
3044
|
+
const response = new WriteResponse({
|
|
2856
3045
|
diagnosticInfos: undefined,
|
|
2857
3046
|
results
|
|
2858
3047
|
});
|
|
@@ -2869,8 +3058,11 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2869
3058
|
// subscription services
|
|
2870
3059
|
protected _on_CreateSubscriptionRequest(message: Message, channel: ServerSecureChannelLayer): void {
|
|
2871
3060
|
const engine = this.engine;
|
|
2872
|
-
const addressSpace = engine.addressSpace
|
|
2873
|
-
|
|
3061
|
+
const addressSpace = engine.addressSpace;
|
|
3062
|
+
if (!addressSpace) {
|
|
3063
|
+
g_sendError(channel, message, CreateSubscriptionResponse, StatusCodes.BadSessionClosed);
|
|
3064
|
+
return;
|
|
3065
|
+
}
|
|
2874
3066
|
const request = message.request as CreateSubscriptionRequest;
|
|
2875
3067
|
assert(request instanceof CreateSubscriptionRequest);
|
|
2876
3068
|
|
|
@@ -2915,7 +3107,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2915
3107
|
channel,
|
|
2916
3108
|
async (session: ServerSession, subscriptionId: number) => {
|
|
2917
3109
|
let subscription = this.engine.findOrphanSubscription(subscriptionId);
|
|
2918
|
-
//
|
|
3110
|
+
// c8 ignore next
|
|
2919
3111
|
if (subscription) {
|
|
2920
3112
|
warningLog("Deleting an orphan subscription", subscriptionId);
|
|
2921
3113
|
|
|
@@ -2960,7 +3152,11 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2960
3152
|
|
|
2961
3153
|
protected _on_CreateMonitoredItemsRequest(message: Message, channel: ServerSecureChannelLayer): void {
|
|
2962
3154
|
const engine = this.engine;
|
|
2963
|
-
const addressSpace = engine.addressSpace
|
|
3155
|
+
const addressSpace = engine.addressSpace;
|
|
3156
|
+
if (!addressSpace) {
|
|
3157
|
+
g_sendError(channel, message, CreateMonitoredItemsResponse, StatusCodes.BadSessionClosed);
|
|
3158
|
+
return;
|
|
3159
|
+
}
|
|
2964
3160
|
|
|
2965
3161
|
const request = message.request as CreateMonitoredItemsRequest;
|
|
2966
3162
|
assert(request instanceof CreateMonitoredItemsRequest);
|
|
@@ -2970,7 +3166,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2970
3166
|
message,
|
|
2971
3167
|
channel,
|
|
2972
3168
|
async (
|
|
2973
|
-
|
|
3169
|
+
_session: ServerSession,
|
|
2974
3170
|
subscription: Subscription,
|
|
2975
3171
|
sendResponse: (response: Response) => void,
|
|
2976
3172
|
sendError: (statusCode: StatusCode) => void
|
|
@@ -2999,7 +3195,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2999
3195
|
monitoredItemCreateRequest
|
|
3000
3196
|
);
|
|
3001
3197
|
if (monitoredItem) {
|
|
3002
|
-
|
|
3198
|
+
if (options.onCreateMonitoredItem) {
|
|
3199
|
+
await options.onCreateMonitoredItem(subscription, monitoredItem);
|
|
3200
|
+
}
|
|
3003
3201
|
subscription.postCreateMonitoredItem(monitoredItem, monitoredItemCreateRequest, createResult);
|
|
3004
3202
|
}
|
|
3005
3203
|
return createResult;
|
|
@@ -3037,10 +3235,10 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3037
3235
|
message,
|
|
3038
3236
|
channel,
|
|
3039
3237
|
async (
|
|
3040
|
-
|
|
3238
|
+
_session: ServerSession,
|
|
3041
3239
|
subscription: Subscription,
|
|
3042
3240
|
sendResponse: (response: ModifySubscriptionResponse) => void,
|
|
3043
|
-
|
|
3241
|
+
_sendError: (statusCode: StatusCode) => void
|
|
3044
3242
|
) => {
|
|
3045
3243
|
subscription.modify(request);
|
|
3046
3244
|
|
|
@@ -3064,7 +3262,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3064
3262
|
message,
|
|
3065
3263
|
channel,
|
|
3066
3264
|
async (
|
|
3067
|
-
|
|
3265
|
+
_session: ServerSession,
|
|
3068
3266
|
subscription: Subscription,
|
|
3069
3267
|
sendResponse: (response: ModifyMonitoredItemsResponse) => void,
|
|
3070
3268
|
sendError: (statusCode: StatusCode) => void
|
|
@@ -3078,7 +3276,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3078
3276
|
return sendError(StatusCodes.BadNothingToDo);
|
|
3079
3277
|
}
|
|
3080
3278
|
|
|
3081
|
-
/*
|
|
3279
|
+
/* c8 ignore next */
|
|
3082
3280
|
if (this.engine.serverCapabilities.operationLimits.maxMonitoredItemsPerCall > 0) {
|
|
3083
3281
|
if (request.itemsToModify.length > this.engine.serverCapabilities.operationLimits.maxMonitoredItemsPerCall) {
|
|
3084
3282
|
return sendError(StatusCodes.BadTooManyOperations);
|
|
@@ -3091,7 +3289,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3091
3289
|
const monitoredItemId = item.monitoredItemId;
|
|
3092
3290
|
const monitoredItem = subscription.getMonitoredItem(monitoredItemId);
|
|
3093
3291
|
if (!monitoredItem) {
|
|
3094
|
-
return new MonitoredItemModifyResult({
|
|
3292
|
+
return new MonitoredItemModifyResult({
|
|
3293
|
+
statusCode: StatusCodes.BadMonitoredItemIdInvalid
|
|
3294
|
+
});
|
|
3095
3295
|
}
|
|
3096
3296
|
|
|
3097
3297
|
// adjust samplingInterval if === -1
|
|
@@ -3119,7 +3319,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3119
3319
|
PublishResponse,
|
|
3120
3320
|
message,
|
|
3121
3321
|
channel,
|
|
3122
|
-
(session: ServerSession, sendResponse: (response: Response) => void,
|
|
3322
|
+
(session: ServerSession, sendResponse: (response: Response) => void, _sendError: (statusCode: StatusCode) => void) => {
|
|
3123
3323
|
assert(session);
|
|
3124
3324
|
assert(session.publishEngine); // server.publishEngine doesn't exists, OPCUAServer has probably shut down already
|
|
3125
3325
|
session.publishEngine._on_PublishRequest(request, (_request1, response) => {
|
|
@@ -3137,7 +3337,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3137
3337
|
SetPublishingModeResponse,
|
|
3138
3338
|
message,
|
|
3139
3339
|
channel,
|
|
3140
|
-
async (
|
|
3340
|
+
async (_session: ServerSession, subscription: Subscription) => {
|
|
3141
3341
|
return subscription.setPublishingMode(publishingEnabled);
|
|
3142
3342
|
}
|
|
3143
3343
|
);
|
|
@@ -3152,17 +3352,17 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3152
3352
|
message,
|
|
3153
3353
|
channel,
|
|
3154
3354
|
async (
|
|
3155
|
-
|
|
3355
|
+
_session: ServerSession,
|
|
3156
3356
|
subscription: Subscription,
|
|
3157
3357
|
sendResponse: (response: Response) => void,
|
|
3158
3358
|
sendError: (statusCode: StatusCode) => void
|
|
3159
3359
|
) => {
|
|
3160
|
-
/*
|
|
3360
|
+
/* c8 ignore next */
|
|
3161
3361
|
if (!request.monitoredItemIds || request.monitoredItemIds.length === 0) {
|
|
3162
3362
|
return sendError(StatusCodes.BadNothingToDo);
|
|
3163
3363
|
}
|
|
3164
3364
|
|
|
3165
|
-
/*
|
|
3365
|
+
/* c8 ignore next */
|
|
3166
3366
|
if (this.engine.serverCapabilities.operationLimits.maxMonitoredItemsPerCall > 0) {
|
|
3167
3367
|
if (request.monitoredItemIds.length > this.engine.serverCapabilities.operationLimits.maxMonitoredItemsPerCall) {
|
|
3168
3368
|
return sendError(StatusCodes.BadTooManyOperations);
|
|
@@ -3204,7 +3404,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3204
3404
|
message,
|
|
3205
3405
|
channel,
|
|
3206
3406
|
async (
|
|
3207
|
-
|
|
3407
|
+
_session: ServerSession,
|
|
3208
3408
|
subscription: Subscription,
|
|
3209
3409
|
sendResponse: (response: Response) => void,
|
|
3210
3410
|
sendError: (statusCode: StatusCode) => void
|
|
@@ -3232,7 +3432,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3232
3432
|
linksToRemove
|
|
3233
3433
|
);
|
|
3234
3434
|
if (statusCode.isNotGood()) {
|
|
3235
|
-
const response = new ServiceFault({
|
|
3435
|
+
const response = new ServiceFault({
|
|
3436
|
+
responseHeader: { serviceResult: statusCode }
|
|
3437
|
+
});
|
|
3236
3438
|
sendResponse(response);
|
|
3237
3439
|
} else {
|
|
3238
3440
|
const response = new SetTriggeringResponse({
|
|
@@ -3253,7 +3455,11 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3253
3455
|
if (!this.options.onDeleteMonitoredItem) {
|
|
3254
3456
|
return;
|
|
3255
3457
|
}
|
|
3256
|
-
|
|
3458
|
+
const t = this.options.onDeleteMonitoredItem.bind(null, subscription);
|
|
3459
|
+
const functor = async (monitoredItem: MonitoredItem) => {
|
|
3460
|
+
await t(monitoredItem);
|
|
3461
|
+
};
|
|
3462
|
+
await subscription.applyOnMonitoredItem(functor);
|
|
3257
3463
|
}
|
|
3258
3464
|
protected _on_RepublishRequest(message: Message, channel: ServerSecureChannelLayer): void {
|
|
3259
3465
|
const request = message.request as RepublishRequest;
|
|
@@ -3264,7 +3470,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3264
3470
|
message,
|
|
3265
3471
|
channel,
|
|
3266
3472
|
async (
|
|
3267
|
-
|
|
3473
|
+
_session: ServerSession,
|
|
3268
3474
|
subscription: Subscription,
|
|
3269
3475
|
sendResponse: (response: Response) => void,
|
|
3270
3476
|
sendError: (statusCode: StatusCode) => void
|
|
@@ -3306,17 +3512,17 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3306
3512
|
message,
|
|
3307
3513
|
channel,
|
|
3308
3514
|
async (
|
|
3309
|
-
|
|
3515
|
+
_session: ServerSession,
|
|
3310
3516
|
subscription: Subscription,
|
|
3311
3517
|
sendResponse: (response: Response) => void,
|
|
3312
3518
|
sendError: (statusCode: StatusCode) => void
|
|
3313
3519
|
) => {
|
|
3314
|
-
/*
|
|
3520
|
+
/* c8 ignore next */
|
|
3315
3521
|
if (!request.monitoredItemIds || request.monitoredItemIds.length === 0) {
|
|
3316
3522
|
return sendError(StatusCodes.BadNothingToDo);
|
|
3317
3523
|
}
|
|
3318
3524
|
|
|
3319
|
-
/*
|
|
3525
|
+
/* c8 ignore next */
|
|
3320
3526
|
if (this.engine.serverCapabilities.operationLimits.maxMonitoredItemsPerCall > 0) {
|
|
3321
3527
|
if (request.monitoredItemIds.length > this.engine.serverCapabilities.operationLimits.maxMonitoredItemsPerCall) {
|
|
3322
3528
|
return sendError(StatusCodes.BadTooManyOperations);
|
|
@@ -3355,7 +3561,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3355
3561
|
message,
|
|
3356
3562
|
channel,
|
|
3357
3563
|
async (
|
|
3358
|
-
|
|
3564
|
+
_session: ServerSession,
|
|
3359
3565
|
sendResponse: (response: Response) => void,
|
|
3360
3566
|
sendError: (statusCode: StatusCode) => void
|
|
3361
3567
|
) => {
|
|
@@ -3380,7 +3586,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3380
3586
|
});
|
|
3381
3587
|
sendResponse(response);
|
|
3382
3588
|
})
|
|
3383
|
-
.catch((
|
|
3589
|
+
.catch((_err) => {
|
|
3384
3590
|
sendError(StatusCodes.BadInternalError);
|
|
3385
3591
|
});
|
|
3386
3592
|
}
|
|
@@ -3401,8 +3607,6 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3401
3607
|
message,
|
|
3402
3608
|
channel,
|
|
3403
3609
|
(session: ServerSession, sendResponse: (response: Response) => void, sendError: (statusCode: StatusCode) => void) => {
|
|
3404
|
-
let response;
|
|
3405
|
-
|
|
3406
3610
|
if (!request.methodsToCall || request.methodsToCall.length === 0) {
|
|
3407
3611
|
return sendError(StatusCodes.BadNothingToDo);
|
|
3408
3612
|
}
|
|
@@ -3423,7 +3627,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3423
3627
|
filterDiagnosticInfo(request.requestHeader.returnDiagnostics, response);
|
|
3424
3628
|
sendResponse(response);
|
|
3425
3629
|
})
|
|
3426
|
-
.catch((
|
|
3630
|
+
.catch((_err) => {
|
|
3427
3631
|
sendError(StatusCodes.BadInternalError);
|
|
3428
3632
|
});
|
|
3429
3633
|
}
|
|
@@ -3495,49 +3699,49 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3495
3699
|
);
|
|
3496
3700
|
}
|
|
3497
3701
|
|
|
3498
|
-
/*
|
|
3702
|
+
/* c8 ignore next */
|
|
3499
3703
|
protected _on_Cancel(message: Message, channel: ServerSecureChannelLayer): void {
|
|
3500
|
-
|
|
3704
|
+
g_sendError(channel, message, CancelResponse, StatusCodes.BadServiceUnsupported);
|
|
3501
3705
|
}
|
|
3502
3706
|
|
|
3503
3707
|
// NodeManagement Service Set Overview
|
|
3504
3708
|
// This Service Set defines Services to add and delete AddressSpace Nodes and References between them. All added
|
|
3505
3709
|
// Nodes continue to exist in the AddressSpace even if the Client that created them disconnects from the Server.
|
|
3506
3710
|
//
|
|
3507
|
-
/*
|
|
3711
|
+
/* c8 ignore next */
|
|
3508
3712
|
protected _on_AddNodes(message: Message, channel: ServerSecureChannelLayer): void {
|
|
3509
|
-
|
|
3713
|
+
g_sendError(channel, message, AddNodesResponse, StatusCodes.BadServiceUnsupported);
|
|
3510
3714
|
}
|
|
3511
3715
|
|
|
3512
|
-
/*
|
|
3716
|
+
/* c8 ignore next */
|
|
3513
3717
|
protected _on_AddReferences(message: Message, channel: ServerSecureChannelLayer): void {
|
|
3514
|
-
|
|
3718
|
+
g_sendError(channel, message, AddReferencesResponse, StatusCodes.BadServiceUnsupported);
|
|
3515
3719
|
}
|
|
3516
3720
|
|
|
3517
|
-
/*
|
|
3721
|
+
/* c8 ignore next */
|
|
3518
3722
|
protected _on_DeleteNodes(message: Message, channel: ServerSecureChannelLayer): void {
|
|
3519
|
-
|
|
3723
|
+
g_sendError(channel, message, DeleteNodesResponse, StatusCodes.BadServiceUnsupported);
|
|
3520
3724
|
}
|
|
3521
3725
|
|
|
3522
|
-
/*
|
|
3726
|
+
/* c8 ignore next */
|
|
3523
3727
|
protected _on_DeleteReferences(message: Message, channel: ServerSecureChannelLayer): void {
|
|
3524
|
-
|
|
3728
|
+
g_sendError(channel, message, DeleteReferencesResponse, StatusCodes.BadServiceUnsupported);
|
|
3525
3729
|
}
|
|
3526
3730
|
|
|
3527
3731
|
// Query Service
|
|
3528
|
-
/*
|
|
3732
|
+
/* c8 ignore next */
|
|
3529
3733
|
protected _on_QueryFirst(message: Message, channel: ServerSecureChannelLayer): void {
|
|
3530
|
-
|
|
3734
|
+
g_sendError(channel, message, QueryFirstResponse, StatusCodes.BadServiceUnsupported);
|
|
3531
3735
|
}
|
|
3532
3736
|
|
|
3533
|
-
/*
|
|
3737
|
+
/* c8 ignore next */
|
|
3534
3738
|
protected _on_QueryNext(message: Message, channel: ServerSecureChannelLayer): void {
|
|
3535
|
-
|
|
3739
|
+
g_sendError(channel, message, QueryNextResponse, StatusCodes.BadServiceUnsupported);
|
|
3536
3740
|
}
|
|
3537
3741
|
|
|
3538
|
-
/*
|
|
3742
|
+
/* c8 ignore next */
|
|
3539
3743
|
protected _on_HistoryUpdate(message: Message, channel: ServerSecureChannelLayer): void {
|
|
3540
|
-
|
|
3744
|
+
g_sendError(channel, message, HistoryUpdateResponse, StatusCodes.BadServiceUnsupported);
|
|
3541
3745
|
}
|
|
3542
3746
|
|
|
3543
3747
|
private createEndpoint(
|
|
@@ -3573,7 +3777,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3573
3777
|
serverOption: OPCUAServerOptions,
|
|
3574
3778
|
endpointOptions: OPCUAServerEndpointOptions
|
|
3575
3779
|
): OPCUAServerEndPoint {
|
|
3576
|
-
/*
|
|
3780
|
+
/* c8 ignore next */
|
|
3577
3781
|
if (!endpointOptions) {
|
|
3578
3782
|
throw new Error("internal error");
|
|
3579
3783
|
}
|
|
@@ -3581,10 +3785,10 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3581
3785
|
endpointOptions.hostname = endpointOptions.hostname || hostname;
|
|
3582
3786
|
endpointOptions.port = endpointOptions.port === undefined ? 26543 : endpointOptions.port;
|
|
3583
3787
|
|
|
3584
|
-
/*
|
|
3788
|
+
/* c8 ignore next */
|
|
3585
3789
|
if (
|
|
3586
|
-
!Object.
|
|
3587
|
-
!isFinite(endpointOptions.port
|
|
3790
|
+
!Object.hasOwn(endpointOptions, "port") ||
|
|
3791
|
+
!Number.isFinite(endpointOptions.port) ||
|
|
3588
3792
|
typeof endpointOptions.port !== "number"
|
|
3589
3793
|
) {
|
|
3590
3794
|
throw new Error(
|
|
@@ -3597,10 +3801,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3597
3801
|
const endPoint = this.createEndpoint(port, serverOption);
|
|
3598
3802
|
|
|
3599
3803
|
endpointOptions.alternateHostname = endpointOptions.alternateHostname || [];
|
|
3600
|
-
const alternateHostname =
|
|
3601
|
-
endpointOptions.alternateHostname
|
|
3602
|
-
|
|
3603
|
-
: [endpointOptions.alternateHostname];
|
|
3804
|
+
const alternateHostname = Array.isArray(endpointOptions.alternateHostname)
|
|
3805
|
+
? endpointOptions.alternateHostname
|
|
3806
|
+
: [endpointOptions.alternateHostname];
|
|
3604
3807
|
const allowAnonymous = endpointOptions.allowAnonymous === undefined ? true : !!endpointOptions.allowAnonymous;
|
|
3605
3808
|
|
|
3606
3809
|
endPoint.addStandardEndpointDescriptions({
|
|
@@ -3614,7 +3817,9 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3614
3817
|
|
|
3615
3818
|
disableDiscovery: !!endpointOptions.disableDiscovery,
|
|
3616
3819
|
// xx hostname,
|
|
3617
|
-
resourcePath: serverOption.resourcePath || ""
|
|
3820
|
+
resourcePath: serverOption.resourcePath || "",
|
|
3821
|
+
|
|
3822
|
+
advertisedEndpoints: endpointOptions.advertisedEndpoints,
|
|
3618
3823
|
|
|
3619
3824
|
// TODO userTokenTypes: endpointOptions.userTokenTypes || undefined,
|
|
3620
3825
|
|
|
@@ -3627,6 +3832,90 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3627
3832
|
await super.initializeCM();
|
|
3628
3833
|
await this.userCertificateManager.initialize();
|
|
3629
3834
|
}
|
|
3835
|
+
|
|
3836
|
+
public on(event: "create_session", eventHandler: (session: ServerSession) => void): this;
|
|
3837
|
+
|
|
3838
|
+
public on(event: "session_activated", eventHandler: (session: ServerSession) => void): this;
|
|
3839
|
+
|
|
3840
|
+
public on(event: "session_closed", eventHandler: (session: ServerSession, reason: string) => void): this;
|
|
3841
|
+
|
|
3842
|
+
public on(event: "post_initialize", eventHandler: () => void): this;
|
|
3843
|
+
|
|
3844
|
+
/**
|
|
3845
|
+
* emitted when the server is trying to registered the LDS
|
|
3846
|
+
* but when the connection to the lds has failed
|
|
3847
|
+
* serverRegistrationPending is sent when the backoff signal of the
|
|
3848
|
+
* connection process is raised
|
|
3849
|
+
* @event serverRegistrationPending
|
|
3850
|
+
*/
|
|
3851
|
+
public on(event: "serverRegistrationPending", eventHandler: () => void): this;
|
|
3852
|
+
|
|
3853
|
+
/**
|
|
3854
|
+
* event raised when server has been successfully registered on the local discovery server
|
|
3855
|
+
* @event serverRegistered
|
|
3856
|
+
*/
|
|
3857
|
+
public on(event: "serverRegistered", eventHandler: () => void): this;
|
|
3858
|
+
|
|
3859
|
+
/**
|
|
3860
|
+
* event raised when server registration has been successfully renewed on the local discovery server
|
|
3861
|
+
* @event serverRegistered
|
|
3862
|
+
*/
|
|
3863
|
+
public on(event: "serverRegistrationRenewed", eventHandler: () => void): this;
|
|
3864
|
+
|
|
3865
|
+
/**
|
|
3866
|
+
* event raised when server has been successfully unregistered from the local discovery server
|
|
3867
|
+
* @event serverUnregistered
|
|
3868
|
+
*/
|
|
3869
|
+
public on(event: "serverUnregistered", eventHandler: () => void): this;
|
|
3870
|
+
|
|
3871
|
+
/**
|
|
3872
|
+
* event raised after the server has raised an OPCUA event toward a client
|
|
3873
|
+
*/
|
|
3874
|
+
public on(event: "event", eventHandler: (eventData: unknown) => void): this;
|
|
3875
|
+
|
|
3876
|
+
/**
|
|
3877
|
+
* event raised when the server received a request from one of its connected client.
|
|
3878
|
+
* useful for trace purpose.
|
|
3879
|
+
*/
|
|
3880
|
+
public on(event: "request", eventHandler: (request: Request, channel: ServerSecureChannelLayer) => void): this;
|
|
3881
|
+
|
|
3882
|
+
/**
|
|
3883
|
+
* event raised when the server send an response to a request to one of its connected client.
|
|
3884
|
+
* useful for trace purpose.
|
|
3885
|
+
*/
|
|
3886
|
+
public on(event: "response", eventHandler: (request: Response, channel: ServerSecureChannelLayer) => void): this;
|
|
3887
|
+
|
|
3888
|
+
/**
|
|
3889
|
+
* event raised when a new secure channel is opened
|
|
3890
|
+
*/
|
|
3891
|
+
public on(event: "newChannel", eventHandler: (channel: ServerSecureChannelLayer, endpoint: OPCUAServerEndPoint) => void): this;
|
|
3892
|
+
|
|
3893
|
+
/**
|
|
3894
|
+
* event raised when a new secure channel is closed
|
|
3895
|
+
*/
|
|
3896
|
+
public on(
|
|
3897
|
+
event: "closeChannel",
|
|
3898
|
+
eventHandler: (channel: ServerSecureChannelLayer, endpoint: OPCUAServerEndPoint) => void
|
|
3899
|
+
): this;
|
|
3900
|
+
|
|
3901
|
+
/**
|
|
3902
|
+
* event raised when the server refused a tcp connection from a client. ( for instance because too any connections)
|
|
3903
|
+
*/
|
|
3904
|
+
public on(event: "connectionRefused", eventHandler: (socketData: ISocketData, endpoint: OPCUAServerEndPoint) => void): this;
|
|
3905
|
+
|
|
3906
|
+
/**
|
|
3907
|
+
* event raised when a OpenSecureChannel has failed, it could be a invalid certificate or malformed message
|
|
3908
|
+
*/
|
|
3909
|
+
public on(
|
|
3910
|
+
event: "openSecureChannelFailure",
|
|
3911
|
+
eventHandler: (socketData: ISocketData, channelData: IChannelData, endpoint: OPCUAServerEndPoint) => void
|
|
3912
|
+
): this;
|
|
3913
|
+
|
|
3914
|
+
public on(event: string, eventHandler: (...args: unknown[]) => void): this;
|
|
3915
|
+
// biome-ignore lint/suspicious/noExplicitAny: must match EventEmitter.on() signature
|
|
3916
|
+
public on(event: string, eventHandler: (...args: any[]) => void): this {
|
|
3917
|
+
return super.on(event, eventHandler);
|
|
3918
|
+
}
|
|
3630
3919
|
}
|
|
3631
3920
|
|
|
3632
3921
|
const userIdentityTokenPasswordRemoved = (userIdentityToken?: UserIdentityToken): UserIdentityToken => {
|
|
@@ -3683,7 +3972,10 @@ function raiseAuditActivateSessionEventType(this: OPCUAServer, session: ServerSe
|
|
|
3683
3972
|
// use the same identifier in all AuditEvents related to the Session Service Set
|
|
3684
3973
|
// (AuditCreateSessionEventType, AuditActivateSessionEventType and their subtypes) and
|
|
3685
3974
|
// the SecureChannel Service Set (AuditChannelEventType and its subtypes).
|
|
3686
|
-
secureChannelId: {
|
|
3975
|
+
secureChannelId: {
|
|
3976
|
+
dataType: "String",
|
|
3977
|
+
value: session.channel?.channelId?.toString() ?? ""
|
|
3978
|
+
}
|
|
3687
3979
|
});
|
|
3688
3980
|
}
|
|
3689
3981
|
}
|
|
@@ -3706,7 +3998,7 @@ export interface RaiseEventAuditEventData extends RaiseEventData {
|
|
|
3706
3998
|
|
|
3707
3999
|
export interface RaiseEventAuditUpdateMethodEventData extends RaiseEventAuditEventData {
|
|
3708
4000
|
methodId: PseudoVariantNodeId;
|
|
3709
|
-
inputArguments:
|
|
4001
|
+
inputArguments: PseudoVariant | Variant | UAEventType | undefined;
|
|
3710
4002
|
}
|
|
3711
4003
|
|
|
3712
4004
|
export interface RaiseEventAuditConditionCommentEventData extends RaiseEventAuditUpdateMethodEventData {
|
|
@@ -3722,6 +4014,7 @@ export interface RaiseEventAuditSessionEventData extends RaiseEventAuditEventDat
|
|
|
3722
4014
|
}
|
|
3723
4015
|
|
|
3724
4016
|
export interface RaiseEventAuditCreateSessionEventData extends RaiseEventAuditSessionEventData {
|
|
4017
|
+
sessionId: PseudoVariantNodeId;
|
|
3725
4018
|
/**
|
|
3726
4019
|
* part 5 - 6.4.8 AuditCreateSessionEventType
|
|
3727
4020
|
* SecureChannelId shall uniquely identify the SecureChannel.
|
|
@@ -3841,115 +4134,7 @@ export interface RaiseAuditCertificateRevokedEventData extends RaiseAuditCertifi
|
|
|
3841
4134
|
* There are no additional Properties defined for this EventType
|
|
3842
4135
|
*/
|
|
3843
4136
|
export interface RaiseAuditCertificateMismatchEventData extends RaiseAuditCertificateEventData { }
|
|
3844
|
-
export interface OPCUAServer {
|
|
3845
|
-
/**
|
|
3846
|
-
* @internal
|
|
3847
|
-
* @param eventType
|
|
3848
|
-
* @param options
|
|
3849
|
-
*/
|
|
3850
|
-
raiseEvent(eventType: "AuditSessionEventType", options: RaiseEventAuditSessionEventData): void;
|
|
3851
|
-
|
|
3852
|
-
raiseEvent(eventType: "AuditCreateSessionEventType", options: RaiseEventAuditCreateSessionEventData): void;
|
|
3853
|
-
|
|
3854
|
-
raiseEvent(eventType: "AuditActivateSessionEventType", options: RaiseEventAuditActivateSessionEventData): void;
|
|
3855
|
-
|
|
3856
|
-
raiseEvent(eventType: "AuditCreateSessionEventType", options: RaiseEventData): void;
|
|
3857
|
-
|
|
3858
|
-
raiseEvent(eventType: "AuditConditionCommentEventType", options: RaiseEventAuditConditionCommentEventData): void;
|
|
3859
|
-
|
|
3860
|
-
raiseEvent(eventType: "AuditUrlMismatchEventType", options: RaiseEventAuditUrlMismatchEventTypeData): void;
|
|
3861
|
-
|
|
3862
|
-
raiseEvent(eventType: "TransitionEventType", options: RaiseEventTransitionEventData): void;
|
|
3863
|
-
|
|
3864
|
-
raiseEvent(eventType: "AuditCertificateInvalidEventType", options: RaiseAuditCertificateInvalidEventData): void;
|
|
3865
|
-
raiseEvent(eventType: "AuditCertificateExpiredEventType", options: RaiseAuditCertificateExpiredEventData): void;
|
|
3866
|
-
raiseEvent(eventType: "AuditCertificateUntrustedEventType", options: RaiseAuditCertificateUntrustedEventData): void;
|
|
3867
|
-
raiseEvent(eventType: "AuditCertificateRevokedEventType", options: RaiseAuditCertificateRevokedEventData): void;
|
|
3868
|
-
raiseEvent(eventType: "AuditCertificateMismatchEventType", options: RaiseAuditCertificateMismatchEventData): void;
|
|
3869
|
-
raiseEvent(eventType: UAEventType, options: RaiseEventData): void;
|
|
3870
|
-
}
|
|
3871
|
-
|
|
3872
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
3873
|
-
export interface OPCUAServer extends EventEmitter {
|
|
3874
|
-
on(event: "create_session", eventHandler: (session: ServerSession) => void): this;
|
|
3875
|
-
|
|
3876
|
-
on(event: "session_activated", eventHandler: (session: ServerSession) => void): this;
|
|
3877
|
-
|
|
3878
|
-
on(event: "session_closed", eventHandler: (session: ServerSession, reason: string) => void): this;
|
|
3879
|
-
|
|
3880
|
-
on(event: "post_initialize", eventHandler: () => void): this;
|
|
3881
|
-
|
|
3882
|
-
/**
|
|
3883
|
-
* emitted when the server is trying to registered the LDS
|
|
3884
|
-
* but when the connection to the lds has failed
|
|
3885
|
-
* serverRegistrationPending is sent when the backoff signal of the
|
|
3886
|
-
* connection process is raised
|
|
3887
|
-
* @event serverRegistrationPending
|
|
3888
|
-
*/
|
|
3889
|
-
on(event: "serverRegistrationPending", eventHandler: () => void): this;
|
|
3890
|
-
|
|
3891
|
-
/**
|
|
3892
|
-
* event raised when server has been successfully registered on the local discovery server
|
|
3893
|
-
* @event serverRegistered
|
|
3894
|
-
*/
|
|
3895
|
-
on(event: "serverRegistered", eventHandler: () => void): this;
|
|
3896
|
-
|
|
3897
|
-
/**
|
|
3898
|
-
* event raised when server registration has been successfully renewed on the local discovery server
|
|
3899
|
-
* @event serverRegistered
|
|
3900
|
-
*/
|
|
3901
|
-
on(event: "serverRegistrationRenewed", eventHandler: () => void): this;
|
|
3902
|
-
|
|
3903
|
-
/**
|
|
3904
|
-
* event raised when server has been successfully unregistered from the local discovery server
|
|
3905
|
-
* @event serverUnregistered
|
|
3906
|
-
*/
|
|
3907
|
-
on(event: "serverUnregistered", eventHandler: () => void): this;
|
|
3908
|
-
|
|
3909
|
-
/**
|
|
3910
|
-
* event raised after the server has raised an OPCUA event toward a client
|
|
3911
|
-
*/
|
|
3912
|
-
on(event: "event", eventHandler: (eventData: any) => void): this;
|
|
3913
|
-
|
|
3914
|
-
/**
|
|
3915
|
-
* event raised when the server received a request from one of its connected client.
|
|
3916
|
-
* useful for trace purpose.
|
|
3917
|
-
*/
|
|
3918
|
-
on(event: "request", eventHandler: (request: Request, channel: ServerSecureChannelLayer) => void): this;
|
|
3919
|
-
|
|
3920
|
-
/**
|
|
3921
|
-
* event raised when the server send an response to a request to one of its connected client.
|
|
3922
|
-
* useful for trace purpose.
|
|
3923
|
-
*/
|
|
3924
|
-
on(event: "response", eventHandler: (request: Response, channel: ServerSecureChannelLayer) => void): this;
|
|
3925
|
-
|
|
3926
|
-
/**
|
|
3927
|
-
* event raised when a new secure channel is opened
|
|
3928
|
-
*/
|
|
3929
|
-
on(event: "newChannel", eventHandler: (channel: ServerSecureChannelLayer, endpoint: OPCUAServerEndPoint) => void): this;
|
|
3930
|
-
|
|
3931
|
-
/**
|
|
3932
|
-
* event raised when a new secure channel is closed
|
|
3933
|
-
*/
|
|
3934
|
-
on(event: "closeChannel", eventHandler: (channel: ServerSecureChannelLayer, endpoint: OPCUAServerEndPoint) => void): this;
|
|
3935
|
-
|
|
3936
|
-
/**
|
|
3937
|
-
* event raised when the server refused a tcp connection from a client. ( for instance because too any connections)
|
|
3938
|
-
*/
|
|
3939
|
-
on(event: "connectionRefused", eventHandler: (socketData: ISocketData, endpoint: OPCUAServerEndPoint) => void): this;
|
|
3940
|
-
|
|
3941
|
-
/**
|
|
3942
|
-
* event raised when a OpenSecureChannel has failed, it could be a invalid certificate or malformed message
|
|
3943
|
-
*/
|
|
3944
|
-
on(
|
|
3945
|
-
event: "openSecureChannelFailure",
|
|
3946
|
-
eventHandler: (socketData: ISocketData, channelData: IChannelData, endpoint: OPCUAServerEndPoint) => void
|
|
3947
|
-
): this;
|
|
3948
|
-
|
|
3949
|
-
on(event: string, eventHandler: (...args: [any?, ...any[]]) => void): this;
|
|
3950
|
-
}
|
|
3951
4137
|
|
|
3952
4138
|
const opts = { multiArgs: false };
|
|
3953
|
-
OPCUAServer.prototype.start = withCallback(OPCUAServer.prototype.start, opts);
|
|
3954
4139
|
OPCUAServer.prototype.initialize = withCallback(OPCUAServer.prototype.initialize, opts);
|
|
3955
4140
|
OPCUAServer.prototype.shutdown = withCallback(OPCUAServer.prototype.shutdown, opts);
|