node-opcua-client 2.98.0 → 2.98.2
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/alarms_and_conditions/client_alarm.d.ts +49 -0
- package/dist/alarms_and_conditions/client_alarm.js +110 -0
- package/dist/alarms_and_conditions/client_alarm.js.map +1 -0
- package/dist/alarms_and_conditions/client_alarm_list.d.ts +25 -0
- package/dist/alarms_and_conditions/client_alarm_list.js +98 -0
- package/dist/alarms_and_conditions/client_alarm_list.js.map +1 -0
- package/dist/alarms_and_conditions/client_alarm_tools.d.ts +4 -0
- package/dist/alarms_and_conditions/client_alarm_tools.js +157 -0
- package/dist/alarms_and_conditions/client_alarm_tools.js.map +1 -0
- package/dist/alarms_and_conditions/client_alarm_tools_acknowledge_all_conditions.d.ts +18 -0
- package/dist/alarms_and_conditions/client_alarm_tools_acknowledge_all_conditions.js +193 -0
- package/dist/alarms_and_conditions/client_alarm_tools_acknowledge_all_conditions.js.map +1 -0
- package/dist/alarms_and_conditions/client_alarm_tools_dump_event.d.ts +9 -0
- package/dist/alarms_and_conditions/client_alarm_tools_dump_event.js +73 -0
- package/dist/alarms_and_conditions/client_alarm_tools_dump_event.js.map +1 -0
- package/dist/alarms_and_conditions/client_alarm_tools_extractConditionFields.d.ts +3 -0
- package/dist/alarms_and_conditions/client_alarm_tools_extractConditionFields.js +119 -0
- package/dist/alarms_and_conditions/client_alarm_tools_extractConditionFields.js.map +1 -0
- package/dist/alarms_and_conditions/client_tools.d.ts +7 -0
- package/dist/alarms_and_conditions/client_tools.js +180 -0
- package/dist/alarms_and_conditions/client_tools.js.map +1 -0
- package/dist/client_base.d.ts +277 -0
- package/dist/client_base.js +15 -0
- package/dist/client_base.js.map +1 -0
- package/dist/client_monitored_item.d.ts +21 -0
- package/dist/client_monitored_item.js +12 -0
- package/dist/client_monitored_item.js.map +1 -0
- package/dist/client_monitored_item_base.d.ts +39 -0
- package/dist/client_monitored_item_base.js +3 -0
- package/dist/client_monitored_item_base.js.map +1 -0
- package/dist/client_monitored_item_group.d.ts +21 -0
- package/dist/client_monitored_item_group.js +14 -0
- package/dist/client_monitored_item_group.js.map +1 -0
- package/dist/client_monitored_item_toolbox.d.ts +15 -0
- package/dist/client_monitored_item_toolbox.js +117 -0
- package/dist/client_monitored_item_toolbox.js.map +1 -0
- package/dist/client_session.d.ts +529 -0
- package/dist/client_session.js +11 -0
- package/dist/client_session.js.map +1 -0
- package/dist/client_session_keepalive_manager.d.ts +34 -0
- package/dist/client_session_keepalive_manager.js +150 -0
- package/dist/client_session_keepalive_manager.js.map +1 -0
- package/dist/client_subscription.d.ts +241 -0
- package/dist/client_subscription.js +15 -0
- package/dist/client_subscription.js.map +1 -0
- package/dist/client_utils.d.ts +22 -0
- package/dist/client_utils.js +88 -0
- package/dist/client_utils.js.map +1 -0
- package/dist/common.d.ts +17 -0
- package/dist/common.js +6 -0
- package/dist/common.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/opcua_client.d.ts +118 -0
- package/dist/opcua_client.js +34 -0
- package/dist/opcua_client.js.map +1 -0
- package/dist/private/client_base_impl.d.ts +160 -0
- package/dist/private/client_base_impl.js +1349 -0
- package/dist/private/client_base_impl.js.map +1 -0
- package/dist/private/client_monitored_item_base_impl.d.ts +3 -0
- package/dist/private/client_monitored_item_base_impl.js +5 -0
- package/dist/private/client_monitored_item_base_impl.js.map +1 -0
- package/dist/private/client_monitored_item_group_impl.d.ts +53 -0
- package/dist/private/client_monitored_item_group_impl.js +162 -0
- package/dist/private/client_monitored_item_group_impl.js.map +1 -0
- package/dist/private/client_monitored_item_impl.d.ts +93 -0
- package/dist/private/client_monitored_item_impl.js +305 -0
- package/dist/private/client_monitored_item_impl.js.map +1 -0
- package/dist/private/client_publish_engine.d.ts +62 -0
- package/dist/private/client_publish_engine.js +409 -0
- package/dist/private/client_publish_engine.js.map +1 -0
- package/dist/private/client_session_impl.d.ts +631 -0
- package/dist/private/client_session_impl.js +1349 -0
- package/dist/private/client_session_impl.js.map +1 -0
- package/dist/private/client_subscription_impl.d.ts +93 -0
- package/dist/private/client_subscription_impl.js +780 -0
- package/dist/private/client_subscription_impl.js.map +1 -0
- package/dist/private/i_private_client.d.ts +21 -0
- package/dist/private/i_private_client.js +3 -0
- package/dist/private/i_private_client.js.map +1 -0
- package/dist/private/opcua_client_impl.d.ts +166 -0
- package/dist/private/opcua_client_impl.js +1001 -0
- package/dist/private/opcua_client_impl.js.map +1 -0
- package/dist/private/performance.d.ts +1 -0
- package/dist/private/performance.js +19 -0
- package/dist/private/performance.js.map +1 -0
- package/dist/reconnection.d.ts +9 -0
- package/dist/reconnection.js +382 -0
- package/dist/reconnection.js.map +1 -0
- package/dist/tools/findservers.d.ts +20 -0
- package/dist/tools/findservers.js +62 -0
- package/dist/tools/findservers.js.map +1 -0
- package/dist/tools/read_history_server_capabilities.d.ts +6 -0
- package/dist/tools/read_history_server_capabilities.js +99 -0
- package/dist/tools/read_history_server_capabilities.js.map +1 -0
- package/dist/user_identity_info.d.ts +1 -0
- package/dist/user_identity_info.js +3 -0
- package/dist/user_identity_info.js.map +1 -0
- package/dist/verify.d.ts +5 -0
- package/dist/verify.js +135 -0
- package/dist/verify.js.map +1 -0
- package/package.json +48 -44
- package/.nycrc.json +0 -14
- package/test_helpers/create_certificates.js +0 -1
- package/typedoc.js +0 -20
|
@@ -0,0 +1,1001 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module node-opcua-client-private
|
|
4
|
+
*/
|
|
5
|
+
// tslint:disable:variable-name
|
|
6
|
+
// tslint:disable:no-console
|
|
7
|
+
// tslint:disable:no-empty
|
|
8
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
9
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
10
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
11
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
12
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
13
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
14
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.OPCUAClientImpl = void 0;
|
|
19
|
+
const crypto = require("crypto");
|
|
20
|
+
const crypto_1 = require("crypto");
|
|
21
|
+
const util_1 = require("util");
|
|
22
|
+
const async = require("async");
|
|
23
|
+
const chalk = require("chalk");
|
|
24
|
+
const node_opcua_assert_1 = require("node-opcua-assert");
|
|
25
|
+
const node_opcua_buffer_utils_1 = require("node-opcua-buffer-utils");
|
|
26
|
+
const node_opcua_crypto_1 = require("node-opcua-crypto");
|
|
27
|
+
const node_opcua_data_model_1 = require("node-opcua-data-model");
|
|
28
|
+
const node_opcua_debug_1 = require("node-opcua-debug");
|
|
29
|
+
const node_opcua_hostname_1 = require("node-opcua-hostname");
|
|
30
|
+
const node_opcua_secure_channel_1 = require("node-opcua-secure-channel");
|
|
31
|
+
const node_opcua_service_endpoints_1 = require("node-opcua-service-endpoints");
|
|
32
|
+
const node_opcua_service_secure_channel_1 = require("node-opcua-service-secure-channel");
|
|
33
|
+
const node_opcua_service_session_1 = require("node-opcua-service-session");
|
|
34
|
+
const node_opcua_status_code_1 = require("node-opcua-status-code");
|
|
35
|
+
const node_opcua_utils_1 = require("node-opcua-utils");
|
|
36
|
+
const opcua_client_1 = require("../opcua_client");
|
|
37
|
+
const reconnection_1 = require("../reconnection");
|
|
38
|
+
const client_base_impl_1 = require("./client_base_impl");
|
|
39
|
+
const client_session_impl_1 = require("./client_session_impl");
|
|
40
|
+
const client_subscription_impl_1 = require("./client_subscription_impl");
|
|
41
|
+
const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
|
|
42
|
+
const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
|
|
43
|
+
const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
|
|
44
|
+
const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
|
|
45
|
+
function validateServerNonce(serverNonce) {
|
|
46
|
+
return !(serverNonce && serverNonce.length < 32) || (serverNonce && serverNonce.length === 0);
|
|
47
|
+
}
|
|
48
|
+
function verifyEndpointDescriptionMatches(client, responseServerEndpoints) {
|
|
49
|
+
// The Server returns its EndpointDescriptions in the response. Clients use this information to
|
|
50
|
+
// determine whether the list of EndpointDescriptions returned from the Discovery Endpoint matches
|
|
51
|
+
// the Endpoints that the Server has. If there is a difference then the Client shall close the
|
|
52
|
+
// Session and report an error.
|
|
53
|
+
// The Server returns all EndpointDescriptions for the serverUri
|
|
54
|
+
// specified by the Client in the request. The Client only verifies EndpointDescriptions with a
|
|
55
|
+
// transportProfileUri that matches the profileUri specified in the original GetEndpoints request.
|
|
56
|
+
// A Client may skip this check if the EndpointDescriptions were provided by a trusted source
|
|
57
|
+
// such as the Administrator.
|
|
58
|
+
// serverEndpoints:
|
|
59
|
+
// The Client shall verify this list with the list from a Discovery Endpoint if it used a Discovery Endpoint
|
|
60
|
+
// fetch to the EndpointDescriptions.
|
|
61
|
+
// ToDo
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
function findUserTokenPolicy(endpointDescription, userTokenType) {
|
|
65
|
+
endpointDescription.userIdentityTokens = endpointDescription.userIdentityTokens || [];
|
|
66
|
+
const r = endpointDescription.userIdentityTokens.filter((userIdentity) => userIdentity.tokenType === userTokenType);
|
|
67
|
+
return r.length === 0 ? null : r[0];
|
|
68
|
+
}
|
|
69
|
+
function createAnonymousIdentityToken(context) {
|
|
70
|
+
const endpoint = context.endpoint;
|
|
71
|
+
const userTokenPolicy = findUserTokenPolicy(endpoint, node_opcua_service_endpoints_1.UserTokenType.Anonymous);
|
|
72
|
+
if (!userTokenPolicy) {
|
|
73
|
+
throw new Error("Cannot find ANONYMOUS user token policy in end point description");
|
|
74
|
+
}
|
|
75
|
+
return new node_opcua_service_session_1.AnonymousIdentityToken({ policyId: userTokenPolicy.policyId });
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
*
|
|
79
|
+
* @param context
|
|
80
|
+
* @param certificate - the user certificate
|
|
81
|
+
* @param privateKey - the private key associated with the user certificate
|
|
82
|
+
*/
|
|
83
|
+
function createX509IdentityToken(context, certificate, privateKey) {
|
|
84
|
+
const endpoint = context.endpoint;
|
|
85
|
+
(0, node_opcua_assert_1.assert)(endpoint instanceof node_opcua_service_endpoints_1.EndpointDescription);
|
|
86
|
+
const userTokenPolicy = findUserTokenPolicy(endpoint, node_opcua_service_endpoints_1.UserTokenType.Certificate);
|
|
87
|
+
// istanbul ignore next
|
|
88
|
+
if (!userTokenPolicy) {
|
|
89
|
+
throw new Error("Cannot find Certificate (X509) user token policy in end point description");
|
|
90
|
+
}
|
|
91
|
+
let securityPolicy = (0, node_opcua_secure_channel_1.fromURI)(userTokenPolicy.securityPolicyUri);
|
|
92
|
+
// if the security policy is not specified we use the session security policy
|
|
93
|
+
if (securityPolicy === node_opcua_secure_channel_1.SecurityPolicy.Invalid) {
|
|
94
|
+
securityPolicy = context.securityPolicy;
|
|
95
|
+
}
|
|
96
|
+
const userIdentityToken = new node_opcua_service_session_1.X509IdentityToken({
|
|
97
|
+
certificateData: certificate,
|
|
98
|
+
policyId: userTokenPolicy.policyId
|
|
99
|
+
});
|
|
100
|
+
const serverCertificate = context.serverCertificate;
|
|
101
|
+
(0, node_opcua_assert_1.assert)(serverCertificate instanceof Buffer);
|
|
102
|
+
const serverNonce = context.serverNonce || Buffer.alloc(0);
|
|
103
|
+
(0, node_opcua_assert_1.assert)(serverNonce instanceof Buffer);
|
|
104
|
+
// see Release 1.02 155 OPC Unified Architecture, Part 4
|
|
105
|
+
const cryptoFactory = (0, node_opcua_secure_channel_1.getCryptoFactory)(securityPolicy);
|
|
106
|
+
// istanbul ignore next
|
|
107
|
+
if (!cryptoFactory) {
|
|
108
|
+
throw new Error(" Unsupported security Policy");
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* OPCUA Spec 1.04 - part 4
|
|
112
|
+
* page 28:
|
|
113
|
+
* 5.6.3.1
|
|
114
|
+
* ...
|
|
115
|
+
* If the token is an X509IdentityToken then the proof is a signature generated with private key
|
|
116
|
+
* associated with the Certificate. The data to sign is created by appending the last serverNonce to
|
|
117
|
+
* the **serverCertificate** specified in the CreateSession response. If a token includes a secret then it
|
|
118
|
+
* should be encrypted using the public key from the serverCertificate.
|
|
119
|
+
*
|
|
120
|
+
* page 155:
|
|
121
|
+
* Token Encryption and Proof of Possession
|
|
122
|
+
* 7.36.2.1 Overview
|
|
123
|
+
* The Client shall always prove possession of a UserIdentityToken when it passes it to the Server.
|
|
124
|
+
* Some tokens include a secret such as a password which the Server will accept as proof. In order
|
|
125
|
+
* to protect these secrets the Token may be encrypted before it is passed to the Server. Other types
|
|
126
|
+
* of tokens allow the Client to create a signature with the secret associated with the Token. In these
|
|
127
|
+
* cases, the Client proves possession of a UserIdentityToken by creating a signature with the secret
|
|
128
|
+
* and passing it to the Server
|
|
129
|
+
*
|
|
130
|
+
* page 159:
|
|
131
|
+
* 7.36.5 X509IdentityTokens
|
|
132
|
+
* The X509IdentityToken is used to pass an X.509 v3 Certificate which is issued by the user.
|
|
133
|
+
* This token shall always be accompanied by a Signature in the userTokenSignature parameter of
|
|
134
|
+
* ActivateSession if required by the SecurityPolicy. The Server should specify a SecurityPolicy for
|
|
135
|
+
* the UserTokenPolicy if the SecureChannel has a SecurityPolicy of None.
|
|
136
|
+
*/
|
|
137
|
+
// now create the proof of possession, by creating a signature
|
|
138
|
+
// The data to sign is created by appending the last serverNonce to the serverCertificate
|
|
139
|
+
// The signature generated with private key associated with the User Certificate
|
|
140
|
+
const userTokenSignature = (0, node_opcua_secure_channel_1.computeSignature)(serverCertificate, serverNonce, privateKey, securityPolicy);
|
|
141
|
+
return { userIdentityToken, userTokenSignature };
|
|
142
|
+
}
|
|
143
|
+
function createUserNameIdentityToken(session, userName, password) {
|
|
144
|
+
// assert(endpoint instanceof EndpointDescription);
|
|
145
|
+
(0, node_opcua_assert_1.assert)(userName === null || typeof userName === "string");
|
|
146
|
+
(0, node_opcua_assert_1.assert)(password === null || typeof password === "string");
|
|
147
|
+
const endpoint = session.endpoint;
|
|
148
|
+
(0, node_opcua_assert_1.assert)(endpoint instanceof node_opcua_service_endpoints_1.EndpointDescription);
|
|
149
|
+
/**
|
|
150
|
+
* OPC Unified Architecture 1.0.4: Part 4 155
|
|
151
|
+
* Each UserIdentityToken allowed by an Endpoint shall have a UserTokenPolicy specified in the
|
|
152
|
+
* EndpointDescription. The UserTokenPolicy specifies what SecurityPolicy to use when encrypting
|
|
153
|
+
* or signing. If this SecurityPolicy is omitted then the Client uses the SecurityPolicy in the
|
|
154
|
+
* EndpointDescription. If the matching SecurityPolicy is set to None then no encryption or signature
|
|
155
|
+
* is required.
|
|
156
|
+
*
|
|
157
|
+
*/
|
|
158
|
+
const userTokenPolicy = findUserTokenPolicy(endpoint, node_opcua_service_endpoints_1.UserTokenType.UserName);
|
|
159
|
+
// istanbul ignore next
|
|
160
|
+
if (!userTokenPolicy) {
|
|
161
|
+
throw new Error("Cannot find USERNAME user token policy in end point description");
|
|
162
|
+
}
|
|
163
|
+
let securityPolicy = (0, node_opcua_secure_channel_1.fromURI)(userTokenPolicy.securityPolicyUri);
|
|
164
|
+
// if the security policy is not specified we use the session security policy
|
|
165
|
+
if (securityPolicy === node_opcua_secure_channel_1.SecurityPolicy.Invalid) {
|
|
166
|
+
securityPolicy = session.securityPolicy;
|
|
167
|
+
}
|
|
168
|
+
let identityToken;
|
|
169
|
+
let serverCertificate = session.serverCertificate;
|
|
170
|
+
// if server does not provide certificate use unencrypted password
|
|
171
|
+
if (!serverCertificate) {
|
|
172
|
+
identityToken = new node_opcua_service_session_1.UserNameIdentityToken({
|
|
173
|
+
encryptionAlgorithm: null,
|
|
174
|
+
password: Buffer.from(password, "utf-8"),
|
|
175
|
+
policyId: userTokenPolicy ? userTokenPolicy.policyId : null,
|
|
176
|
+
userName
|
|
177
|
+
});
|
|
178
|
+
return identityToken;
|
|
179
|
+
}
|
|
180
|
+
(0, node_opcua_assert_1.assert)(serverCertificate instanceof Buffer);
|
|
181
|
+
serverCertificate = (0, node_opcua_crypto_1.toPem)(serverCertificate, "CERTIFICATE");
|
|
182
|
+
const publicKey = (0, crypto_1.createPublicKey)((0, node_opcua_crypto_1.extractPublicKeyFromCertificateSync)(serverCertificate));
|
|
183
|
+
const serverNonce = session.serverNonce || Buffer.alloc(0);
|
|
184
|
+
(0, node_opcua_assert_1.assert)(serverNonce instanceof Buffer);
|
|
185
|
+
// If None is specified for the UserTokenPolicy and SecurityPolicy is None
|
|
186
|
+
// then the password only contains the UTF-8 encoded password.
|
|
187
|
+
// note: this means that password is sent in clear text to the server
|
|
188
|
+
// note: OPCUA specification discourages use of unencrypted password
|
|
189
|
+
// but some old OPCUA server may only provide this policy and we
|
|
190
|
+
// still have to support in the client?
|
|
191
|
+
if (securityPolicy === node_opcua_secure_channel_1.SecurityPolicy.None) {
|
|
192
|
+
identityToken = new node_opcua_service_session_1.UserNameIdentityToken({
|
|
193
|
+
encryptionAlgorithm: null,
|
|
194
|
+
password: Buffer.from(password, "utf-8"),
|
|
195
|
+
policyId: userTokenPolicy.policyId,
|
|
196
|
+
userName
|
|
197
|
+
});
|
|
198
|
+
return identityToken;
|
|
199
|
+
}
|
|
200
|
+
// see Release 1.02 155 OPC Unified Architecture, Part 4
|
|
201
|
+
const cryptoFactory = (0, node_opcua_secure_channel_1.getCryptoFactory)(securityPolicy);
|
|
202
|
+
// istanbul ignore next
|
|
203
|
+
if (!cryptoFactory) {
|
|
204
|
+
throw new Error(" Unsupported security Policy " + securityPolicy.toString());
|
|
205
|
+
}
|
|
206
|
+
identityToken = new node_opcua_service_session_1.UserNameIdentityToken({
|
|
207
|
+
encryptionAlgorithm: cryptoFactory.asymmetricEncryptionAlgorithm,
|
|
208
|
+
password: Buffer.from(password, "utf-8"),
|
|
209
|
+
policyId: userTokenPolicy.policyId,
|
|
210
|
+
userName
|
|
211
|
+
});
|
|
212
|
+
// now encrypt password as requested
|
|
213
|
+
const lenBuf = (0, node_opcua_buffer_utils_1.createFastUninitializedBuffer)(4);
|
|
214
|
+
lenBuf.writeUInt32LE(identityToken.password.length + serverNonce.length, 0);
|
|
215
|
+
const block = Buffer.concat([lenBuf, identityToken.password, serverNonce]);
|
|
216
|
+
identityToken.password = cryptoFactory.asymmetricEncrypt(block, publicKey);
|
|
217
|
+
return identityToken;
|
|
218
|
+
}
|
|
219
|
+
function _adjustRevisedSessionTimeout(revisedSessionTimeout, requestedTimeout) {
|
|
220
|
+
// Some old OPCUA Servers are known to report an invalid revisedSessionTimeout
|
|
221
|
+
// such as Siemens SimoCode Pro V.
|
|
222
|
+
// we need to adjust the value here, by guessing a sensible sessionTimeout value to use instead.
|
|
223
|
+
if (revisedSessionTimeout < 1e-10) {
|
|
224
|
+
warningLog(`the revisedSessionTimeout ${revisedSessionTimeout} reported by the server is inconsistent and has been adjusted back to requestedTimeout ${requestedTimeout}`);
|
|
225
|
+
return requestedTimeout;
|
|
226
|
+
}
|
|
227
|
+
if (revisedSessionTimeout < OPCUAClientImpl.minimumRevisedSessionTimeout) {
|
|
228
|
+
warningLog(`the revisedSessionTimeout ${revisedSessionTimeout} is smaller than the minimum timeout (OPCUAClientImpl.minimumRevisedSessionTimeout = ${OPCUAClientImpl.minimumRevisedSessionTimeout}) and has been clamped to this value`);
|
|
229
|
+
return OPCUAClientImpl.minimumRevisedSessionTimeout;
|
|
230
|
+
}
|
|
231
|
+
return revisedSessionTimeout;
|
|
232
|
+
}
|
|
233
|
+
/***
|
|
234
|
+
* @constructor
|
|
235
|
+
* @internal
|
|
236
|
+
*/
|
|
237
|
+
class OPCUAClientImpl extends client_base_impl_1.ClientBaseImpl {
|
|
238
|
+
static create(options) {
|
|
239
|
+
return new OPCUAClientImpl(options);
|
|
240
|
+
}
|
|
241
|
+
constructor(options) {
|
|
242
|
+
options = options || {};
|
|
243
|
+
super(options);
|
|
244
|
+
// @property endpointMustExist {Boolean}
|
|
245
|
+
// if set to true , create Session will only accept connection from server which endpoint_url has been reported
|
|
246
|
+
// by GetEndpointsRequest.
|
|
247
|
+
// By default, the client is strict.
|
|
248
|
+
if (Object.prototype.hasOwnProperty.call(options, "endpoint_must_exist")) {
|
|
249
|
+
if (Object.prototype.hasOwnProperty.call(options, "endpointMustExist")) {
|
|
250
|
+
throw new Error("endpoint_must_exist is deprecated! you must now use endpointMustExist instead of endpoint_must_exist ");
|
|
251
|
+
}
|
|
252
|
+
warningLog("Warning: endpoint_must_exist is now deprecated, use endpointMustExist instead");
|
|
253
|
+
options.endpointMustExist = options.endpoint_must_exist;
|
|
254
|
+
}
|
|
255
|
+
this.endpointMustExist = (0, node_opcua_utils_1.isNullOrUndefined)(options.endpointMustExist) ? true : !!options.endpointMustExist;
|
|
256
|
+
this.requestedSessionTimeout = options.requestedSessionTimeout || 60000; // 1 minute
|
|
257
|
+
this.___sessionName_counter = 0;
|
|
258
|
+
this.endpoint = undefined;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* @internal
|
|
262
|
+
* @param args
|
|
263
|
+
*
|
|
264
|
+
*/
|
|
265
|
+
createSession(...args) {
|
|
266
|
+
if (args.length === 1) {
|
|
267
|
+
return this.createSession({ type: node_opcua_service_endpoints_1.UserTokenType.Anonymous }, args[0]);
|
|
268
|
+
}
|
|
269
|
+
const userIdentityInfo = args[0] || { type: node_opcua_service_endpoints_1.UserTokenType.Anonymous };
|
|
270
|
+
const callback = args[1];
|
|
271
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
272
|
+
this._createSession((err, session) => {
|
|
273
|
+
if (err) {
|
|
274
|
+
callback(err);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
/* istanbul ignore next */
|
|
278
|
+
if (!session) {
|
|
279
|
+
return callback(new Error("Internal Error"));
|
|
280
|
+
}
|
|
281
|
+
this._addSession(session);
|
|
282
|
+
this._activateSession(session, userIdentityInfo, (err1, session2) => {
|
|
283
|
+
callback(err1, session2);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
createSession2(...args) {
|
|
289
|
+
if (args.length === 1) {
|
|
290
|
+
return this.createSession2({ type: node_opcua_service_endpoints_1.UserTokenType.Anonymous }, args[0]);
|
|
291
|
+
}
|
|
292
|
+
const userIdentityInfo = args[0];
|
|
293
|
+
const callback = args[1];
|
|
294
|
+
if (!this._secureChannel) {
|
|
295
|
+
// we do not have a connection anymore
|
|
296
|
+
return callback(new Error("Connection is closed"));
|
|
297
|
+
}
|
|
298
|
+
return this.createSession(args[0], (err, session) => {
|
|
299
|
+
if (err && err.message.match(/BadTooManySessions/)) {
|
|
300
|
+
const delayToRetry = 5; // seconds
|
|
301
|
+
errorLog(`TooManySession .... we need to retry later ... in ${delayToRetry} secondes`);
|
|
302
|
+
const retryCreateSessionTimer = setTimeout(() => {
|
|
303
|
+
errorLog("TooManySession .... now retrying");
|
|
304
|
+
this.createSession2(userIdentityInfo, callback);
|
|
305
|
+
}, delayToRetry * 1000);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
callback(err, session);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
changeSessionIdentity(...args) {
|
|
312
|
+
warningLog("[NODE-OPCUA-W27] OPCUAClient.changeSessionIdentity(session,userIdentity) is deprecated use ClientSession.changeUser(userIdentity) instead");
|
|
313
|
+
const session = args[0];
|
|
314
|
+
const userIdentityInfo = args[1];
|
|
315
|
+
const callback = args[2];
|
|
316
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
317
|
+
session.changeUser(userIdentityInfo, callback);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* @internals
|
|
321
|
+
* @param args
|
|
322
|
+
*/
|
|
323
|
+
closeSession(...args) {
|
|
324
|
+
super.closeSession(...args);
|
|
325
|
+
}
|
|
326
|
+
toString() {
|
|
327
|
+
let str = client_base_impl_1.ClientBaseImpl.prototype.toString.call(this);
|
|
328
|
+
str += " requestedSessionTimeout....... " + this.requestedSessionTimeout + "\n";
|
|
329
|
+
str += " endpointUrl................... " + this.endpointUrl + "\n";
|
|
330
|
+
str += " serverUri..................... " + this.serverUri + "\n";
|
|
331
|
+
return str;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
*
|
|
337
|
+
* ```javascript
|
|
338
|
+
*
|
|
339
|
+
* const session = await OPCUAClient.createSession(endpointUrl);
|
|
340
|
+
* const dataValue = await session.read({ nodeId, attributeId: AttributeIds.Value });
|
|
341
|
+
* await session.close();
|
|
342
|
+
*
|
|
343
|
+
* ```
|
|
344
|
+
* @stability experimental
|
|
345
|
+
*
|
|
346
|
+
* @param endpointUrl
|
|
347
|
+
* @param userIdentity
|
|
348
|
+
* @returns session
|
|
349
|
+
*
|
|
350
|
+
*
|
|
351
|
+
* const create
|
|
352
|
+
*/
|
|
353
|
+
static createSession(endpointUrl, userIdentity, clientOptions) {
|
|
354
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
355
|
+
const client = opcua_client_1.OPCUAClient.create(clientOptions || {});
|
|
356
|
+
yield client.connect(endpointUrl);
|
|
357
|
+
const session = yield client.createSession2(userIdentity);
|
|
358
|
+
const oldClose = session.close;
|
|
359
|
+
session.close = thenify.withCallback((...args) => {
|
|
360
|
+
if (args.length === 1) {
|
|
361
|
+
return session.close(true, args[0]);
|
|
362
|
+
}
|
|
363
|
+
const deleteSubscriptions = args[0];
|
|
364
|
+
const callback = args[1];
|
|
365
|
+
session.close = oldClose;
|
|
366
|
+
oldClose.call(session, deleteSubscriptions, (err) => {
|
|
367
|
+
client.disconnect((err) => {
|
|
368
|
+
callback(err);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
return session;
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* @internal
|
|
377
|
+
* @param args
|
|
378
|
+
*/
|
|
379
|
+
withSession(connectionPoint, inner_func, ...args) {
|
|
380
|
+
const endpointUrl = typeof connectionPoint === "string" ? connectionPoint : connectionPoint.endpointUrl;
|
|
381
|
+
const userIdentity = typeof connectionPoint === "string" ? { type: node_opcua_service_endpoints_1.UserTokenType.Anonymous } : connectionPoint.userIdentity;
|
|
382
|
+
const callback = args[0];
|
|
383
|
+
(0, node_opcua_assert_1.assert)(typeof inner_func === "function", "expecting inner function");
|
|
384
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function", "expecting callback function");
|
|
385
|
+
let theSession;
|
|
386
|
+
let the_error;
|
|
387
|
+
let need_disconnect = false;
|
|
388
|
+
async.series([
|
|
389
|
+
// step 1 : connect to
|
|
390
|
+
(innerCallback) => {
|
|
391
|
+
this.connect(endpointUrl, (err) => {
|
|
392
|
+
need_disconnect = true;
|
|
393
|
+
if (err) {
|
|
394
|
+
warningLog(err.message);
|
|
395
|
+
errorLog(" cannot connect to endpoint :", endpointUrl);
|
|
396
|
+
}
|
|
397
|
+
innerCallback(err);
|
|
398
|
+
});
|
|
399
|
+
},
|
|
400
|
+
// step 2 : createSession
|
|
401
|
+
(innerCallback) => {
|
|
402
|
+
this.createSession2(userIdentity, (err, session) => {
|
|
403
|
+
if (err) {
|
|
404
|
+
return innerCallback(err);
|
|
405
|
+
}
|
|
406
|
+
if (!session) {
|
|
407
|
+
return innerCallback(new Error("internal error"));
|
|
408
|
+
}
|
|
409
|
+
theSession = session;
|
|
410
|
+
innerCallback();
|
|
411
|
+
});
|
|
412
|
+
},
|
|
413
|
+
(innerCallback) => {
|
|
414
|
+
try {
|
|
415
|
+
inner_func(theSession, (err) => {
|
|
416
|
+
the_error = err;
|
|
417
|
+
innerCallback();
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
catch (err) {
|
|
421
|
+
console.log("Exception catch in inner function, check your code ", err);
|
|
422
|
+
errorLog("OPCUAClientImpl#withClientSession", err.message);
|
|
423
|
+
the_error = err;
|
|
424
|
+
innerCallback();
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
// close session
|
|
428
|
+
(innerCallback) => {
|
|
429
|
+
theSession.close(/*deleteSubscriptions=*/ true, (err) => {
|
|
430
|
+
if (err) {
|
|
431
|
+
debugLog("OPCUAClientImpl#withClientSession: session closed failed ?");
|
|
432
|
+
}
|
|
433
|
+
innerCallback();
|
|
434
|
+
});
|
|
435
|
+
},
|
|
436
|
+
(innerCallback) => {
|
|
437
|
+
this.disconnect((err) => {
|
|
438
|
+
need_disconnect = false;
|
|
439
|
+
if (err) {
|
|
440
|
+
errorLog("OPCUAClientImpl#withClientSession: client disconnect failed ?");
|
|
441
|
+
}
|
|
442
|
+
innerCallback();
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
], (err1) => {
|
|
446
|
+
if (err1) {
|
|
447
|
+
console.log("err", err1.message);
|
|
448
|
+
}
|
|
449
|
+
if (need_disconnect) {
|
|
450
|
+
errorLog("Disconnecting client after failure");
|
|
451
|
+
this.disconnect((err2) => {
|
|
452
|
+
return callback(the_error || err1 || err2);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
return callback(the_error || err1);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
withSubscription(endpointUrl, subscriptionParameters, innerFunc, callback) {
|
|
461
|
+
(0, node_opcua_assert_1.assert)(typeof innerFunc === "function");
|
|
462
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
463
|
+
this.withSession(endpointUrl, (session, done) => {
|
|
464
|
+
(0, node_opcua_assert_1.assert)(typeof done === "function");
|
|
465
|
+
const subscription = new client_subscription_impl_1.ClientSubscriptionImpl(session, subscriptionParameters);
|
|
466
|
+
try {
|
|
467
|
+
innerFunc(session, subscription, (err) => {
|
|
468
|
+
subscription.terminate((err1) => {
|
|
469
|
+
done(err1);
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
catch (err) {
|
|
474
|
+
debugLog(err);
|
|
475
|
+
done(err);
|
|
476
|
+
}
|
|
477
|
+
}, callback);
|
|
478
|
+
}
|
|
479
|
+
withSessionAsync(connectionPoint, func) {
|
|
480
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
481
|
+
(0, node_opcua_assert_1.assert)(typeof func === "function");
|
|
482
|
+
(0, node_opcua_assert_1.assert)(func.length === 1, "expecting a single argument in func");
|
|
483
|
+
const endpointUrl = typeof connectionPoint === "string" ? connectionPoint : connectionPoint.endpointUrl;
|
|
484
|
+
const userIdentity = typeof connectionPoint === "string" ? { type: node_opcua_service_endpoints_1.UserTokenType.Anonymous } : connectionPoint.userIdentity;
|
|
485
|
+
yield this.connect(endpointUrl);
|
|
486
|
+
this.on("backoff", (count, delay) => {
|
|
487
|
+
warningLog("cannot connect to ", endpointUrl, "attempt #" + count, " retrying in ", delay);
|
|
488
|
+
});
|
|
489
|
+
try {
|
|
490
|
+
const session = yield this.createSession2(userIdentity);
|
|
491
|
+
let result;
|
|
492
|
+
try {
|
|
493
|
+
result = yield func(session);
|
|
494
|
+
return result;
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
errorLog(err);
|
|
498
|
+
throw err;
|
|
499
|
+
}
|
|
500
|
+
finally {
|
|
501
|
+
yield session.close();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
catch (err) {
|
|
505
|
+
errorLog(err.message);
|
|
506
|
+
throw err;
|
|
507
|
+
}
|
|
508
|
+
finally {
|
|
509
|
+
yield this.disconnect();
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
withSubscriptionAsync(connectionPoint, parameters, func) {
|
|
514
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
515
|
+
yield this.withSessionAsync(connectionPoint, (session) => __awaiter(this, void 0, void 0, function* () {
|
|
516
|
+
(0, node_opcua_assert_1.assert)(session, " session must exist");
|
|
517
|
+
const subscription = yield session.createSubscription2(parameters);
|
|
518
|
+
try {
|
|
519
|
+
const result = yield func(session, subscription);
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
console.log("withSubscriptionAsync inner function failed ", err.message);
|
|
524
|
+
throw err;
|
|
525
|
+
}
|
|
526
|
+
finally {
|
|
527
|
+
yield subscription.terminate();
|
|
528
|
+
}
|
|
529
|
+
}));
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
reactivateSession(session, callback) {
|
|
533
|
+
const internalSession = session;
|
|
534
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
535
|
+
if (!this._secureChannel) {
|
|
536
|
+
return callback(new Error(" client must be connected first"));
|
|
537
|
+
}
|
|
538
|
+
// istanbul ignore next
|
|
539
|
+
if (!this.__resolveEndPoint() || !this.endpoint) {
|
|
540
|
+
return callback(new Error(" End point must exist " + this._secureChannel.endpointUrl +
|
|
541
|
+
" securityMode = " + node_opcua_service_secure_channel_1.MessageSecurityMode[this.securityMode] +
|
|
542
|
+
" securityPolicy = " + this.securityPolicy));
|
|
543
|
+
}
|
|
544
|
+
(0, node_opcua_assert_1.assert)(!internalSession._client || (0, node_opcua_utils_1.matchUri)(internalSession._client.endpointUrl, this.endpointUrl), "cannot reactivateSession on a different endpoint");
|
|
545
|
+
const old_client = internalSession._client;
|
|
546
|
+
debugLog("OPCUAClientImpl#reactivateSession");
|
|
547
|
+
this._activateSession(internalSession, internalSession.userIdentityInfo, (err /*, newSession?: ClientSessionImpl*/) => {
|
|
548
|
+
if (!err) {
|
|
549
|
+
if (old_client !== this) {
|
|
550
|
+
// remove session from old client:
|
|
551
|
+
if (old_client) {
|
|
552
|
+
old_client._removeSession(internalSession);
|
|
553
|
+
(0, node_opcua_assert_1.assert)(old_client._sessions.indexOf(internalSession) === -1);
|
|
554
|
+
}
|
|
555
|
+
this._addSession(internalSession);
|
|
556
|
+
(0, node_opcua_assert_1.assert)(internalSession._client === this);
|
|
557
|
+
(0, node_opcua_assert_1.assert)(!internalSession._closed, "session should not vbe closed");
|
|
558
|
+
(0, node_opcua_assert_1.assert)(this._sessions.indexOf(internalSession) !== -1);
|
|
559
|
+
}
|
|
560
|
+
callback();
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
// istanbul ignore next
|
|
564
|
+
if (doDebug) {
|
|
565
|
+
debugLog(chalk.red.bgWhite("reactivateSession has failed !"), err.message);
|
|
566
|
+
}
|
|
567
|
+
callback(err);
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* @internal
|
|
573
|
+
* @private
|
|
574
|
+
*/
|
|
575
|
+
_on_connection_reestablished(callback) {
|
|
576
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
577
|
+
// call base class implementation first
|
|
578
|
+
client_base_impl_1.ClientBaseImpl.prototype._on_connection_reestablished.call(this, ( /*err?: Error*/) => {
|
|
579
|
+
(0, reconnection_1.repair_client_sessions)(this, callback);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
*
|
|
584
|
+
* @internal
|
|
585
|
+
* @private
|
|
586
|
+
*/
|
|
587
|
+
__createSession_step3(session, callback) {
|
|
588
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
589
|
+
(0, node_opcua_assert_1.assert)(this.serverUri !== undefined, " must have a valid server URI " + this.serverUri);
|
|
590
|
+
(0, node_opcua_assert_1.assert)(this.endpointUrl !== undefined, " must have a valid server endpointUrl");
|
|
591
|
+
(0, node_opcua_assert_1.assert)(this.endpoint);
|
|
592
|
+
// istanbul ignore next
|
|
593
|
+
if (!this._secureChannel) {
|
|
594
|
+
return callback(new Error("Invalid channel"));
|
|
595
|
+
}
|
|
596
|
+
const applicationUri = this._getApplicationUri();
|
|
597
|
+
const applicationDescription = {
|
|
598
|
+
applicationName: new node_opcua_data_model_1.LocalizedText({ text: this.applicationName, locale: null }),
|
|
599
|
+
applicationType: node_opcua_service_endpoints_1.ApplicationType.Client,
|
|
600
|
+
applicationUri,
|
|
601
|
+
discoveryProfileUri: undefined,
|
|
602
|
+
discoveryUrls: [],
|
|
603
|
+
gatewayServerUri: undefined,
|
|
604
|
+
productUri: "NodeOPCUA-Client"
|
|
605
|
+
};
|
|
606
|
+
// note : do not confuse CreateSessionRequest.clientNonce with OpenSecureChannelRequest.clientNonce
|
|
607
|
+
// which are two different nonce, with different size (although they share the same name )
|
|
608
|
+
this.clientNonce = crypto.randomBytes(32);
|
|
609
|
+
// recycle session name if already exists
|
|
610
|
+
const sessionName = session.name;
|
|
611
|
+
const request = new node_opcua_service_session_1.CreateSessionRequest({
|
|
612
|
+
clientCertificate: this.getCertificate(),
|
|
613
|
+
clientDescription: applicationDescription,
|
|
614
|
+
clientNonce: this.clientNonce,
|
|
615
|
+
endpointUrl: this.endpointUrl,
|
|
616
|
+
maxResponseMessageSize: 800000,
|
|
617
|
+
requestedSessionTimeout: this.requestedSessionTimeout,
|
|
618
|
+
serverUri: this.serverUri,
|
|
619
|
+
sessionName,
|
|
620
|
+
});
|
|
621
|
+
// a client Nonce must be provided if security mode is set
|
|
622
|
+
(0, node_opcua_assert_1.assert)(this._secureChannel.securityMode === node_opcua_service_secure_channel_1.MessageSecurityMode.None || request.clientNonce !== null);
|
|
623
|
+
this.performMessageTransaction(request, (err, response) => {
|
|
624
|
+
/* istanbul ignore next */
|
|
625
|
+
if (err) {
|
|
626
|
+
console.log("__createSession_step3 has failed", err.message);
|
|
627
|
+
return callback(err);
|
|
628
|
+
// //xxxxxx qdqsdqsdqsdqsd
|
|
629
|
+
// // we could have an invalid state here or a connection error
|
|
630
|
+
// errorLog("error: ", err.message, " retrying in ... 5 secondes");
|
|
631
|
+
// setTimeout(() => {
|
|
632
|
+
// errorLog(" .... now retrying");
|
|
633
|
+
// this.__createSession_step3(session, callback);
|
|
634
|
+
// }, 5 * 1000);
|
|
635
|
+
// return;
|
|
636
|
+
}
|
|
637
|
+
/* istanbul ignore next */
|
|
638
|
+
if (!response || !(response instanceof node_opcua_service_session_1.CreateSessionResponse)) {
|
|
639
|
+
return callback(new Error("internal error"));
|
|
640
|
+
}
|
|
641
|
+
if (response.responseHeader.serviceResult === node_opcua_status_code_1.StatusCodes.BadTooManySessions) {
|
|
642
|
+
return callback(new Error(response.responseHeader.serviceResult.toString()));
|
|
643
|
+
}
|
|
644
|
+
if (response.responseHeader.serviceResult !== node_opcua_status_code_1.StatusCodes.Good) {
|
|
645
|
+
err = new Error("Error " + response.responseHeader.serviceResult.name + " " + response.responseHeader.serviceResult.description);
|
|
646
|
+
return callback(err);
|
|
647
|
+
}
|
|
648
|
+
// istanbul ignore next
|
|
649
|
+
if (!validateServerNonce(response.serverNonce)) {
|
|
650
|
+
return callback(new Error("Invalid server Nonce"));
|
|
651
|
+
}
|
|
652
|
+
// todo: verify SignedSoftwareCertificates and response.serverSignature
|
|
653
|
+
session.name = request.sessionName || "";
|
|
654
|
+
session.sessionId = response.sessionId;
|
|
655
|
+
session.authenticationToken = response.authenticationToken;
|
|
656
|
+
session.timeout = _adjustRevisedSessionTimeout(response.revisedSessionTimeout, this.requestedSessionTimeout);
|
|
657
|
+
session.serverNonce = response.serverNonce;
|
|
658
|
+
session.serverCertificate = response.serverCertificate;
|
|
659
|
+
session.serverSignature = response.serverSignature;
|
|
660
|
+
debugLog("revised session timeout = ", session.timeout, response.revisedSessionTimeout);
|
|
661
|
+
response.serverEndpoints = response.serverEndpoints || [];
|
|
662
|
+
if (!verifyEndpointDescriptionMatches(this, response.serverEndpoints)) {
|
|
663
|
+
errorLog("Endpoint description previously retrieved with GetEndpointsDescription");
|
|
664
|
+
errorLog("CreateSessionResponse.serverEndpoints= ");
|
|
665
|
+
errorLog(response.serverEndpoints);
|
|
666
|
+
return callback(new Error("Invalid endpoint descriptions Found"));
|
|
667
|
+
}
|
|
668
|
+
// this._serverEndpoints = response.serverEndpoints;
|
|
669
|
+
session.serverEndpoints = response.serverEndpoints;
|
|
670
|
+
callback(null, session);
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
*
|
|
675
|
+
* @internal
|
|
676
|
+
* @private
|
|
677
|
+
*/
|
|
678
|
+
__createSession_step2(session, callback) {
|
|
679
|
+
(0, util_1.callbackify)(node_opcua_hostname_1.extractFullyQualifiedDomainName)(() => {
|
|
680
|
+
this.__createSession_step3(session, callback);
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* @internal
|
|
685
|
+
* @private
|
|
686
|
+
*/
|
|
687
|
+
_activateSession(session, userIdentityInfo, callback) {
|
|
688
|
+
// see OPCUA Part 4 - $7.35
|
|
689
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
690
|
+
// istanbul ignore next
|
|
691
|
+
if (!this._secureChannel) {
|
|
692
|
+
return callback(new Error(" No secure channel"));
|
|
693
|
+
}
|
|
694
|
+
const serverCertificate = session.serverCertificate;
|
|
695
|
+
// If the securityPolicyUri is None and none of the UserTokenPolicies requires encryption,
|
|
696
|
+
// the Client shall ignore the ApplicationInstanceCertificate (serverCertificate)
|
|
697
|
+
(0, node_opcua_assert_1.assert)(serverCertificate === null || serverCertificate instanceof Buffer);
|
|
698
|
+
const serverNonce = session.serverNonce;
|
|
699
|
+
(0, node_opcua_assert_1.assert)(!serverNonce || serverNonce instanceof Buffer);
|
|
700
|
+
// make sure session is attached to this client
|
|
701
|
+
const _old_client = session._client;
|
|
702
|
+
session._client = this;
|
|
703
|
+
const context = {
|
|
704
|
+
endpoint: this.endpoint,
|
|
705
|
+
securityPolicy: this._secureChannel.securityPolicy,
|
|
706
|
+
serverCertificate,
|
|
707
|
+
serverNonce: serverNonce // please check this !
|
|
708
|
+
};
|
|
709
|
+
this.createUserIdentityToken(context, userIdentityInfo, (err, data) => {
|
|
710
|
+
if (err) {
|
|
711
|
+
session._client = _old_client;
|
|
712
|
+
return callback(err);
|
|
713
|
+
}
|
|
714
|
+
data = data;
|
|
715
|
+
const userIdentityToken = data.userIdentityToken;
|
|
716
|
+
const userTokenSignature = data.userTokenSignature;
|
|
717
|
+
// TODO. fill the ActivateSessionRequest
|
|
718
|
+
// see 5.6.3.2 Parameters OPC Unified Architecture, Part 4 30 Release 1.02
|
|
719
|
+
const request = new node_opcua_service_session_1.ActivateSessionRequest({
|
|
720
|
+
// This is a signature generated with the private key associated with the
|
|
721
|
+
// clientCertificate. The SignatureAlgorithm shall be the AsymmetricSignatureAlgorithm
|
|
722
|
+
// specified in the SecurityPolicy for the Endpoint. The SignatureData type is defined in 7.30.
|
|
723
|
+
clientSignature: this.computeClientSignature(this._secureChannel, serverCertificate, serverNonce) || undefined,
|
|
724
|
+
// These are the SoftwareCertificates which have been issued to the Client application.
|
|
725
|
+
// The productUri contained in the SoftwareCertificates shall match the productUri in the
|
|
726
|
+
// ApplicationDescription passed by the Client in the CreateSession requests. Certificates without
|
|
727
|
+
// matching productUri should be ignored. Servers may reject connections from Clients if they are
|
|
728
|
+
// not satisfied with the SoftwareCertificates provided by the Client.
|
|
729
|
+
// This parameter only needs to be specified in the first ActivateSession request
|
|
730
|
+
// after CreateSession.
|
|
731
|
+
// It shall always be omitted if the maxRequestMessageSize returned from the Server in the
|
|
732
|
+
// CreateSession response is less than one megabyte.
|
|
733
|
+
// The SignedSoftwareCertificate type is defined in 7.31.
|
|
734
|
+
clientSoftwareCertificates: [],
|
|
735
|
+
// List of locale ids in priority order for localized strings. The first LocaleId in the list
|
|
736
|
+
// has the highest priority. If the Server returns a localized string to the Client, the Server
|
|
737
|
+
// shall return the translation with the highest priority that it can. If it does not have a
|
|
738
|
+
// translation for any of the locales identified in this list, then it shall return the string
|
|
739
|
+
// value that it has and include the locale id with the string.
|
|
740
|
+
// See Part 3 for more detail on locale ids. If the Client fails to specify at least one locale id,
|
|
741
|
+
// the Server shall use any that it has.
|
|
742
|
+
// This parameter only needs to be specified during the first call to ActivateSession during
|
|
743
|
+
// a single application Session. If it is not specified the Server shall keep using the current
|
|
744
|
+
// localeIds for the Session.
|
|
745
|
+
localeIds: [],
|
|
746
|
+
// The credentials of the user associated with the Client application. The Server uses these
|
|
747
|
+
// credentials to determine whether the Client should be allowed to activate a Session and what
|
|
748
|
+
// resources the Client has access to during this Session. The UserIdentityToken is an extensible
|
|
749
|
+
// parameter type defined in 7.35.
|
|
750
|
+
// The EndpointDescription specifies what UserIdentityTokens the Server shall accept.
|
|
751
|
+
userIdentityToken,
|
|
752
|
+
// If the Client specified a user identity token that supports digital signatures,
|
|
753
|
+
// then it shall create a signature and pass it as this parameter. Otherwise the parameter
|
|
754
|
+
// is omitted.
|
|
755
|
+
// The SignatureAlgorithm depends on the identity token type.
|
|
756
|
+
userTokenSignature
|
|
757
|
+
});
|
|
758
|
+
request.requestHeader.authenticationToken = session.authenticationToken;
|
|
759
|
+
session.lastRequestSentTime = new Date();
|
|
760
|
+
this.performMessageTransaction(request, (err1, response) => {
|
|
761
|
+
if (!err1 && response && response.responseHeader.serviceResult === node_opcua_status_code_1.StatusCodes.Good) {
|
|
762
|
+
/* istanbul ignore next */
|
|
763
|
+
if (!(response instanceof node_opcua_service_session_1.ActivateSessionResponse)) {
|
|
764
|
+
return callback(new Error("Internal Error"));
|
|
765
|
+
}
|
|
766
|
+
if (!validateServerNonce(response.serverNonce)) {
|
|
767
|
+
return callback(new Error("Invalid server Nonce"));
|
|
768
|
+
}
|
|
769
|
+
session._client = this;
|
|
770
|
+
session.serverNonce = response.serverNonce;
|
|
771
|
+
session.lastResponseReceivedTime = new Date();
|
|
772
|
+
if (this.keepSessionAlive) {
|
|
773
|
+
session.startKeepAliveManager();
|
|
774
|
+
}
|
|
775
|
+
session.userIdentityInfo = userIdentityInfo;
|
|
776
|
+
return callback(null, session);
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
// restore client
|
|
780
|
+
session._client = _old_client;
|
|
781
|
+
/* istanbul ignore next */
|
|
782
|
+
if (!err1 && response) {
|
|
783
|
+
err1 = new Error(response.responseHeader.serviceResult.toString());
|
|
784
|
+
}
|
|
785
|
+
session._client = _old_client;
|
|
786
|
+
return callback(err1);
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
*
|
|
793
|
+
* @private
|
|
794
|
+
*/
|
|
795
|
+
_nextSessionName() {
|
|
796
|
+
if (!this.___sessionName_counter) {
|
|
797
|
+
this.___sessionName_counter = 0;
|
|
798
|
+
}
|
|
799
|
+
this.___sessionName_counter += 1;
|
|
800
|
+
return this.clientName + this.___sessionName_counter;
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
*
|
|
804
|
+
* @private
|
|
805
|
+
*/
|
|
806
|
+
_getApplicationUri() {
|
|
807
|
+
var _a, _b;
|
|
808
|
+
const certificate = this.getCertificate();
|
|
809
|
+
let applicationUri;
|
|
810
|
+
if (certificate) {
|
|
811
|
+
const e = (0, node_opcua_crypto_1.exploreCertificate)(certificate);
|
|
812
|
+
if ((_b = (_a = e.tbsCertificate.extensions) === null || _a === void 0 ? void 0 : _a.subjectAltName) === null || _b === void 0 ? void 0 : _b.uniformResourceIdentifier) {
|
|
813
|
+
applicationUri = e.tbsCertificate.extensions.subjectAltName.uniformResourceIdentifier[0];
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
errorLog("Certificate has no extensions.subjectAltName.uniformResourceIdentifier, ");
|
|
817
|
+
errorLog((0, node_opcua_crypto_1.toPem)(certificate, "CERTIFICATE"));
|
|
818
|
+
applicationUri = this._getBuiltApplicationUri();
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
applicationUri = this._getBuiltApplicationUri();
|
|
823
|
+
}
|
|
824
|
+
return applicationUri;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
*
|
|
828
|
+
* @private
|
|
829
|
+
*/
|
|
830
|
+
__resolveEndPoint() {
|
|
831
|
+
this.securityPolicy = this.securityPolicy || node_opcua_secure_channel_1.SecurityPolicy.None;
|
|
832
|
+
let endpoint = this.findEndpoint(this._secureChannel.endpointUrl, this.securityMode, this.securityPolicy);
|
|
833
|
+
this.endpoint = endpoint;
|
|
834
|
+
// this is explained here : see OPCUA Part 4 Version 1.02 $5.4.1 page 12:
|
|
835
|
+
// A Client shall verify the HostName specified in the Server Certificate is the same as the HostName
|
|
836
|
+
// contained in the endpointUrl provided in the EndpointDescription. If there is a difference then the
|
|
837
|
+
// Client shall report the difference and may close the SecureChannel.
|
|
838
|
+
if (!this.endpoint) {
|
|
839
|
+
if (this.endpointMustExist) {
|
|
840
|
+
warningLog("OPCUAClientImpl#endpointMustExist = true and endpoint with url ", this._secureChannel.endpointUrl, " cannot be found");
|
|
841
|
+
const infos = this._serverEndpoints.map((endpoint) => `${endpoint.endpointUrl} ${node_opcua_service_secure_channel_1.MessageSecurityMode[endpoint.securityMode]}, ${endpoint.securityPolicyUri} `);
|
|
842
|
+
warningLog("Valid endpoints are ");
|
|
843
|
+
warningLog(" " + infos.join("\n "));
|
|
844
|
+
return false;
|
|
845
|
+
}
|
|
846
|
+
else {
|
|
847
|
+
// fallback :
|
|
848
|
+
// our strategy is to take the first server_end_point that match the security settings
|
|
849
|
+
// ( is this really OK ?)
|
|
850
|
+
// this will permit us to access a OPCUA Server using it's IP address instead of its hostname
|
|
851
|
+
endpoint = this.findEndpointForSecurity(this.securityMode, this.securityPolicy);
|
|
852
|
+
if (!endpoint) {
|
|
853
|
+
return false;
|
|
854
|
+
}
|
|
855
|
+
this.endpoint = endpoint;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return true;
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
*
|
|
862
|
+
* @private
|
|
863
|
+
*/
|
|
864
|
+
_createSession(callback) {
|
|
865
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
866
|
+
(0, node_opcua_assert_1.assert)(this._secureChannel);
|
|
867
|
+
if (!this.__resolveEndPoint() || !this.endpoint) {
|
|
868
|
+
/* istanbul ignore next */
|
|
869
|
+
if (this._serverEndpoints) {
|
|
870
|
+
warningLog("server endpoints =", this._serverEndpoints.map((endpoint) => {
|
|
871
|
+
var _a;
|
|
872
|
+
return endpoint.endpointUrl + " "
|
|
873
|
+
+ node_opcua_service_secure_channel_1.MessageSecurityMode[endpoint.securityMode] + " "
|
|
874
|
+
+ endpoint.securityPolicyUri + " "
|
|
875
|
+
+ ((_a = endpoint.userIdentityTokens) === null || _a === void 0 ? void 0 : _a.map((u) => node_opcua_service_endpoints_1.UserTokenType[u.tokenType]).join(","));
|
|
876
|
+
}).join('\n'));
|
|
877
|
+
}
|
|
878
|
+
return callback(new Error(" End point must exist " + this._secureChannel.endpointUrl +
|
|
879
|
+
" securityMode = " + node_opcua_service_secure_channel_1.MessageSecurityMode[this.securityMode] +
|
|
880
|
+
" securityPolicy = " + this.securityPolicy));
|
|
881
|
+
}
|
|
882
|
+
this.serverUri = this.endpoint.server.applicationUri || "invalid application uri";
|
|
883
|
+
this.endpointUrl = this._secureChannel.endpointUrl;
|
|
884
|
+
const session = new client_session_impl_1.ClientSessionImpl(this);
|
|
885
|
+
session.name = this._nextSessionName();
|
|
886
|
+
this.__createSession_step2(session, callback);
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
*
|
|
890
|
+
* @private
|
|
891
|
+
*/
|
|
892
|
+
computeClientSignature(channel, serverCertificate, serverNonce) {
|
|
893
|
+
return (0, node_opcua_secure_channel_1.computeSignature)(serverCertificate, serverNonce || Buffer.alloc(0), this.getPrivateKey(), channel.securityPolicy);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
*
|
|
897
|
+
* @private
|
|
898
|
+
*/
|
|
899
|
+
createUserIdentityToken(context, userIdentityInfo, callback) {
|
|
900
|
+
function coerceUserIdentityInfo(identityInfo) {
|
|
901
|
+
if (!identityInfo) {
|
|
902
|
+
return { type: node_opcua_service_endpoints_1.UserTokenType.Anonymous };
|
|
903
|
+
}
|
|
904
|
+
if (Object.prototype.hasOwnProperty.call(identityInfo, "type")) {
|
|
905
|
+
return identityInfo;
|
|
906
|
+
}
|
|
907
|
+
if (Object.prototype.hasOwnProperty.call(identityInfo, "userName")) {
|
|
908
|
+
identityInfo.type = node_opcua_service_endpoints_1.UserTokenType.UserName;
|
|
909
|
+
return identityInfo;
|
|
910
|
+
}
|
|
911
|
+
if (Object.prototype.hasOwnProperty.call(identityInfo, "certificateData")) {
|
|
912
|
+
identityInfo.type = node_opcua_service_endpoints_1.UserTokenType.Certificate;
|
|
913
|
+
return identityInfo;
|
|
914
|
+
}
|
|
915
|
+
identityInfo.type = node_opcua_service_endpoints_1.UserTokenType.Anonymous;
|
|
916
|
+
return identityInfo;
|
|
917
|
+
}
|
|
918
|
+
userIdentityInfo = coerceUserIdentityInfo(userIdentityInfo);
|
|
919
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
920
|
+
if (null === userIdentityInfo) {
|
|
921
|
+
return callback(null, {
|
|
922
|
+
userIdentityToken: null,
|
|
923
|
+
userTokenSignature: {}
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
let userIdentityToken;
|
|
927
|
+
let userTokenSignature = {
|
|
928
|
+
algorithm: undefined,
|
|
929
|
+
signature: undefined
|
|
930
|
+
};
|
|
931
|
+
try {
|
|
932
|
+
switch (userIdentityInfo.type) {
|
|
933
|
+
case node_opcua_service_endpoints_1.UserTokenType.Anonymous:
|
|
934
|
+
userIdentityToken = createAnonymousIdentityToken(context);
|
|
935
|
+
break;
|
|
936
|
+
case node_opcua_service_endpoints_1.UserTokenType.UserName: {
|
|
937
|
+
const userName = userIdentityInfo.userName || "";
|
|
938
|
+
const password = userIdentityInfo.password || "";
|
|
939
|
+
userIdentityToken = createUserNameIdentityToken(context, userName, password);
|
|
940
|
+
break;
|
|
941
|
+
}
|
|
942
|
+
case node_opcua_service_endpoints_1.UserTokenType.Certificate: {
|
|
943
|
+
const certificate = userIdentityInfo.certificateData;
|
|
944
|
+
const privateKey = crypto.createPrivateKey(userIdentityInfo.privateKey);
|
|
945
|
+
({ userIdentityToken, userTokenSignature } = createX509IdentityToken(context, certificate, privateKey));
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
default:
|
|
949
|
+
debugLog(" userIdentityInfo = ", userIdentityInfo);
|
|
950
|
+
return callback(new Error("CLIENT: Invalid userIdentityInfo"));
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
catch (err) {
|
|
954
|
+
if (typeof err === "string") {
|
|
955
|
+
return callback(new Error("Create identity token failed " + userIdentityInfo.type + " " + err));
|
|
956
|
+
}
|
|
957
|
+
return callback(err);
|
|
958
|
+
}
|
|
959
|
+
return callback(null, { userIdentityToken, userTokenSignature });
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
OPCUAClientImpl.minimumRevisedSessionTimeout = 100.0;
|
|
963
|
+
exports.OPCUAClientImpl = OPCUAClientImpl;
|
|
964
|
+
// tslint:disable:no-var-requires
|
|
965
|
+
// tslint:disable:max-line-length
|
|
966
|
+
const thenify = require("thenify");
|
|
967
|
+
/**
|
|
968
|
+
* @method createSession
|
|
969
|
+
* @async
|
|
970
|
+
*
|
|
971
|
+
* @example
|
|
972
|
+
* // create a anonymous session
|
|
973
|
+
* const session = await client.createSession();
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* // create a session with a userName and password
|
|
977
|
+
* const userIdentityInfo = {
|
|
978
|
+
* type: UserTokenType.UserName,
|
|
979
|
+
* userName: "JoeDoe",
|
|
980
|
+
* password:"secret"
|
|
981
|
+
* };
|
|
982
|
+
* const session = client.createSession(userIdentityInfo);
|
|
983
|
+
*
|
|
984
|
+
*/
|
|
985
|
+
OPCUAClientImpl.prototype.createSession = thenify.withCallback(OPCUAClientImpl.prototype.createSession);
|
|
986
|
+
OPCUAClientImpl.prototype.createSession2 = thenify.withCallback(OPCUAClientImpl.prototype.createSession2);
|
|
987
|
+
/**
|
|
988
|
+
* @method changeSessionIdentity
|
|
989
|
+
* @async
|
|
990
|
+
*/
|
|
991
|
+
OPCUAClientImpl.prototype.changeSessionIdentity = thenify.withCallback(OPCUAClientImpl.prototype.changeSessionIdentity);
|
|
992
|
+
/**
|
|
993
|
+
* @method closeSession
|
|
994
|
+
* @async
|
|
995
|
+
* @example
|
|
996
|
+
* const session = await client.createSession();
|
|
997
|
+
* await client.closeSession(session);
|
|
998
|
+
*/
|
|
999
|
+
OPCUAClientImpl.prototype.closeSession = thenify.withCallback(OPCUAClientImpl.prototype.closeSession);
|
|
1000
|
+
OPCUAClientImpl.prototype.reactivateSession = thenify.withCallback(OPCUAClientImpl.prototype.reactivateSession);
|
|
1001
|
+
//# sourceMappingURL=opcua_client_impl.js.map
|