UNPKG

node-opcua-server

Version:

pure nodejs OPCUA SDK - module server

866 lines 40.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OPCUAServerEndPoint = void 0; /* eslint-disable max-statements */ /** * @module node-opcua-server */ // tslint:disable:no-console const events_1 = require("events"); const net_1 = __importDefault(require("net")); const chalk_1 = __importDefault(require("chalk")); const async_1 = __importDefault(require("async")); const node_opcua_assert_1 = require("node-opcua-assert"); const web_1 = require("node-opcua-crypto/web"); const node_opcua_debug_1 = require("node-opcua-debug"); const node_opcua_hostname_1 = require("node-opcua-hostname"); const node_opcua_secure_channel_1 = require("node-opcua-secure-channel"); const node_opcua_service_endpoints_1 = require("node-opcua-service-endpoints"); const node_opcua_service_endpoints_2 = require("node-opcua-service-endpoints"); const node_opcua_service_endpoints_3 = require("node-opcua-service-endpoints"); const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename); const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename); const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename); const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename); const default_transportProfileUri = "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"; function extractSocketData(socket, reason) { const { bytesRead, bytesWritten, remoteAddress, remoteFamily, remotePort, localAddress, localPort } = socket; const data = { bytesRead, bytesWritten, localAddress, localPort, remoteAddress, remoteFamily, remotePort, timestamp: new Date(), reason }; return data; } function extractChannelData(channel) { const { channelId, clientCertificate, securityMode, securityPolicy, timeout, transactionsCount } = channel; const channelData = { channelId, clientCertificate, securityMode, securityPolicy, timeout, transactionsCount }; return channelData; } function dumpChannelInfo(channels) { function d(s) { return `[ status=${s.status} lastSeen=${s.clientLastContactTime.toFixed(0)}ms sessionName=${s.sessionName} timeout=${s.sessionTimeout} ]`; } function dumpChannel(channel) { console.log("------------------------------------------------------"); console.log(" channelId = ", channel.channelId); console.log(" timeout = ", channel.timeout); console.log(" remoteAddress = ", channel.remoteAddress); console.log(" remotePort = ", channel.remotePort); console.log(""); console.log(" bytesWritten = ", channel.bytesWritten); console.log(" bytesRead = ", channel.bytesRead); console.log(" sessions = ", Object.keys(channel.sessionTokens).length); console.log(Object.values(channel.sessionTokens).map(d).join("\n")); const socket = channel.transport?._socket; if (!socket) { console.log(" SOCKET IS CLOSED"); } } for (const channel of channels) { dumpChannel(channel); } console.log("------------------------------------------------------"); } const emptyCertificate = Buffer.alloc(0); const emptyPrivateKey = null; let OPCUAServerEndPointCounter = 0; function getUniqueName(name, collection) { if (collection[name]) { let counter = 0; while (collection[name + "_" + counter.toString()]) { counter++; } name = name + "_" + counter.toString(); collection[name] = 1; return name; } else { collection[name] = 1; return name; } } /** * OPCUAServerEndPoint a Server EndPoint. * A sever end point is listening to one port * note: * see OPCUA Release 1.03 part 4 page 108 7.1 ApplicationDescription */ class OPCUAServerEndPoint extends events_1.EventEmitter { constructor(options) { super(); this._started = false; this._counter = OPCUAServerEndPointCounter++; this._policy_deduplicator = {}; (0, node_opcua_assert_1.assert)(!Object.prototype.hasOwnProperty.call(options, "certificate"), "expecting a certificateChain instead"); (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(options, "certificateChain"), "expecting a certificateChain"); (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(options, "privateKey")); this.certificateManager = options.certificateManager; options.port = options.port || 0; this.port = parseInt(options.port.toString(), 10); this.host = options.host; (0, node_opcua_assert_1.assert)(typeof this.port === "number"); this._certificateChain = options.certificateChain; this._privateKey = options.privateKey; this._channels = {}; this.defaultSecureTokenLifetime = options.defaultSecureTokenLifetime || 600000; this.maxConnections = options.maxConnections || 20; this.timeout = options.timeout || 30000; this._server = undefined; this._setup_server(); this._endpoints = []; this.objectFactory = options.objectFactory; this.bytesWrittenInOldChannels = 0; this.bytesReadInOldChannels = 0; this.transactionsCountOldChannels = 0; this.securityTokenCountOldChannels = 0; this.serverInfo = options.serverInfo; (0, node_opcua_assert_1.assert)(this.serverInfo !== null && typeof this.serverInfo === "object"); this.transportSettings = options.transportSettings; } dispose() { this._certificateChain = emptyCertificate; this._privateKey = emptyPrivateKey; (0, node_opcua_assert_1.assert)(Object.keys(this._channels).length === 0, "OPCUAServerEndPoint channels must have been deleted"); this._channels = {}; this.serverInfo = new node_opcua_service_endpoints_3.ApplicationDescription({}); this._endpoints = []; (0, node_opcua_assert_1.assert)(this._endpoints.length === 0, "endpoints must have been deleted"); this._endpoints = []; this._server = undefined; this._listen_callback = undefined; this.removeAllListeners(); } toString() { const txt = " end point" + this._counter + " port = " + this.port + " l = " + this._endpoints.length + " " + (0, web_1.makeSHA1Thumbprint)(this.getCertificateChain()).toString("hex"); return txt; } getChannels() { return Object.values(this._channels); } /** * Returns the X509 DER form of the server certificate */ getCertificate() { return (0, web_1.split_der)(this.getCertificateChain())[0]; } /** * Returns the X509 DER form of the server certificate */ getCertificateChain() { return this._certificateChain; } /** * the private key */ getPrivateKey() { return this._privateKey; } /** * The number of active channel on this end point. */ get currentChannelCount() { return Object.keys(this._channels).length; } /** */ getEndpointDescription(securityMode, securityPolicy, endpointUrl) { const endpoints = this.endpointDescriptions(); const arr = endpoints.filter(matching_endpoint.bind(this, securityMode, securityPolicy, endpointUrl)); if (endpointUrl && endpointUrl.length > 0 && !(arr.length === 0 || arr.length === 1)) { errorLog("Several matching endpoints have been found : "); for (const a of arr) { errorLog(" ", a.endpointUrl, node_opcua_secure_channel_1.MessageSecurityMode[securityMode], securityPolicy); } } return arr.length === 0 ? null : arr[0]; } addEndpointDescription(securityMode, securityPolicy, options) { // istanbul ignore next if (securityMode === node_opcua_secure_channel_1.MessageSecurityMode.None && securityPolicy !== node_opcua_secure_channel_1.SecurityPolicy.None) { throw new Error(" invalid security "); } // istanbul ignore next if (securityMode !== node_opcua_secure_channel_1.MessageSecurityMode.None && securityPolicy === node_opcua_secure_channel_1.SecurityPolicy.None) { throw new Error(" invalid security "); } // // resource Path is a string added at the end of the url such as "/UA/Server" const resourcePath = (options.resourcePath || "").replace(/\\/g, "/"); (0, node_opcua_assert_1.assert)(resourcePath.length === 0 || resourcePath.charAt(0) === "/", "resourcePath should start with /"); const hostname = options.hostname || (0, node_opcua_hostname_1.getFullyQualifiedDomainName)(); const endpointUrl = `opc.tcp://${hostname}:${this.port}${resourcePath}`; const endpoint_desc = this.getEndpointDescription(securityMode, securityPolicy, endpointUrl); // istanbul ignore next if (endpoint_desc) { throw new Error(" endpoint already exist"); } const userTokenTypes = options.userTokenTypes; // now build endpointUrl this._endpoints.push(_makeEndpointDescription({ collection: this._policy_deduplicator, hostname, server: this.serverInfo, serverCertificateChain: this.getCertificateChain(), securityMode, securityPolicy, allowUnsecurePassword: options.allowUnsecurePassword, resourcePath: options.resourcePath, restricted: !!options.restricted, securityPolicies: options.securityPolicies || [], userTokenTypes }, this)); } addRestrictedEndpointDescription(options) { options = { ...options }; options.restricted = true; return this.addEndpointDescription(node_opcua_secure_channel_1.MessageSecurityMode.None, node_opcua_secure_channel_1.SecurityPolicy.None, options); } addStandardEndpointDescriptions(options) { options = options || {}; options.securityModes = options.securityModes || defaultSecurityModes; options.securityPolicies = options.securityPolicies || defaultSecurityPolicies; options.userTokenTypes = options.userTokenTypes || defaultUserTokenTypes; options.allowAnonymous = options.allowAnonymous === undefined ? true : options.allowAnonymous; // make sure we do not have anonymous if (!options.allowAnonymous) { options.userTokenTypes = options.userTokenTypes.filter((r) => r !== node_opcua_service_endpoints_1.UserTokenType.Anonymous); } const defaultHostname = options.hostname || (0, node_opcua_hostname_1.getFullyQualifiedDomainName)(); let hostnames = [defaultHostname]; options.alternateHostname = options.alternateHostname || []; if (typeof options.alternateHostname === "string") { options.alternateHostname = [options.alternateHostname]; } // remove duplicates if any (uniq) hostnames = [...new Set(hostnames.concat(options.alternateHostname))]; for (const alternateHostname of hostnames) { const optionsE = { hostname: alternateHostname, securityPolicies: options.securityPolicies, userTokenTypes: options.userTokenTypes, allowUnsecurePassword: options.allowUnsecurePassword, alternateHostname: options.alternateHostname, resourcePath: options.resourcePath }; if (options.securityModes.indexOf(node_opcua_secure_channel_1.MessageSecurityMode.None) >= 0) { this.addEndpointDescription(node_opcua_secure_channel_1.MessageSecurityMode.None, node_opcua_secure_channel_1.SecurityPolicy.None, optionsE); } else { if (!options.disableDiscovery) { this.addRestrictedEndpointDescription(optionsE); } } for (const securityMode of options.securityModes) { if (securityMode === node_opcua_secure_channel_1.MessageSecurityMode.None) { continue; } for (const securityPolicy of options.securityPolicies) { if (securityPolicy === node_opcua_secure_channel_1.SecurityPolicy.None) { continue; } this.addEndpointDescription(securityMode, securityPolicy, optionsE); } } } } /** * returns the list of end point descriptions. */ endpointDescriptions() { return this._endpoints; } /** */ listen(callback) { (0, node_opcua_assert_1.assert)(typeof callback === "function"); (0, node_opcua_assert_1.assert)(!this._started, "OPCUAServerEndPoint is already listening"); this._listen_callback = callback; this._server.on("error", (err) => { debugLog(chalk_1.default.red.bold(" error") + " port = " + this.port, err); this._started = false; this._end_listen(err); }); this._server.on("listening", () => { debugLog("server is listening"); }); const listenOptions = { port: this.port, host: this.host }; this._server.listen(listenOptions, /*"::",*/ (err) => { // 'listening' listener debugLog(chalk_1.default.green.bold("LISTENING TO PORT "), this.port, "err ", err); (0, node_opcua_assert_1.assert)(!err, " cannot listen to port "); this._started = true; if (!this.port) { const add = this._server.address(); this.port = typeof add !== "string" ? add.port : this.port; } this._end_listen(); }); } killClientSockets(callback) { for (const channel of this.getChannels()) { const hacked_channel = channel; if (hacked_channel.transport && hacked_channel.transport._socket) { // hacked_channel.transport._socket.close(); hacked_channel.transport._socket.destroy(); hacked_channel.transport._socket.emit("error", new Error("EPIPE")); } } callback(); } suspendConnection(callback) { if (!this._started) { return callback(new Error("Connection already suspended !!")); } // Stops the server from accepting new connections and keeps existing connections. // (note from nodejs doc: This function is asynchronous, the server is finally closed // when all connections are ended and the server emits a 'close' event. // The optional callback will be called once the 'close' event occurs. // Unlike that event, it will be called with an Error as its only argument // if the server was not open when it was closed. this._server.close(() => { this._started = false; debugLog("Connection has been closed !" + this.port); }); this._started = false; callback(); } restoreConnection(callback) { this.listen(callback); } abruptlyInterruptChannels() { for (const channel of Object.values(this._channels)) { channel.abruptlyInterrupt(); } } /** */ shutdown(callback) { debugLog("OPCUAServerEndPoint#shutdown "); if (this._started) { // make sure we don't accept new connection any more ... this.suspendConnection(() => { // shutdown all opened channels ... const _channels = Object.values(this._channels); async_1.default.each(_channels, (channel, callback1) => { this.shutdown_channel(channel, callback1); }, (err) => { /* istanbul ignore next */ if (!(Object.keys(this._channels).length === 0)) { errorLog(" Bad !"); } (0, node_opcua_assert_1.assert)(Object.keys(this._channels).length === 0, "channel must have unregistered themselves"); callback(err || undefined); }); }); } else { callback(); } } /** */ start(callback) { (0, node_opcua_assert_1.assert)(typeof callback === "function"); this.listen(callback); } get bytesWritten() { const channels = Object.values(this._channels); return (this.bytesWrittenInOldChannels + channels.reduce((accumulated, channel) => { return accumulated + channel.bytesWritten; }, 0)); } get bytesRead() { const channels = Object.values(this._channels); return (this.bytesReadInOldChannels + channels.reduce((accumulated, channel) => { return accumulated + channel.bytesRead; }, 0)); } get transactionsCount() { const channels = Object.values(this._channels); return (this.transactionsCountOldChannels + channels.reduce((accumulated, channel) => { return accumulated + channel.transactionsCount; }, 0)); } get securityTokenCount() { const channels = Object.values(this._channels); return (this.securityTokenCountOldChannels + channels.reduce((accumulated, channel) => { return accumulated + channel.securityTokenCount; }, 0)); } get activeChannelCount() { return Object.keys(this._channels).length; } _dump_statistics() { this._server.getConnections((err, count) => { debugLog(chalk_1.default.cyan("CONCURRENT CONNECTION = "), count); }); debugLog(chalk_1.default.cyan("MAX CONNECTIONS = "), this._server.maxConnections); } _setup_server() { (0, node_opcua_assert_1.assert)(!this._server); this._server = net_1.default.createServer({ pauseOnConnect: true }, this._on_client_connection.bind(this)); // xx console.log(" Server with max connections ", self.maxConnections); this._server.maxConnections = this.maxConnections + 1; // plus one extra this._listen_callback = undefined; this._server .on("connection", (socket) => { // istanbul ignore next if (doDebug) { this._dump_statistics(); debugLog("server connected with : " + socket.remoteAddress + ":" + socket.remotePort); } }) .on("close", () => { debugLog("server closed : all connections have ended"); }) .on("error", (err) => { // this could be because the port is already in use debugLog(chalk_1.default.red.bold("server error: "), err.message); }); } _on_client_connection(socket) { // a client is attempting a connection on the socket socket.setNoDelay(true); debugLog("OPCUAServerEndPoint#_on_client_connection", this._started); if (!this._started) { debugLog(chalk_1.default.bgWhite.cyan("OPCUAServerEndPoint#_on_client_connection " + "SERVER END POINT IS PROBABLY SHUTTING DOWN !!! - Connection is refused")); socket.end(); return; } const deny_connection = () => { console.log(chalk_1.default.bgWhite.cyan("OPCUAServerEndPoint#_on_client_connection " + "The maximum number of connection has been reached - Connection is refused")); const reason = "maxConnections reached (" + this.maxConnections + ")"; const socketData = extractSocketData(socket, reason); this.emit("connectionRefused", socketData); socket.end(); socket.destroy(); }; const establish_connection = () => { const nbConnections = Object.keys(this._channels).length; if (nbConnections >= this.maxConnections) { warningLog(" nbConnections ", nbConnections, " self._server.maxConnections", this._server.maxConnections, this.maxConnections); deny_connection(); return; } debugLog("OPCUAServerEndPoint._on_client_connection successful => New Channel"); const channel = new node_opcua_secure_channel_1.ServerSecureChannelLayer({ defaultSecureTokenLifetime: this.defaultSecureTokenLifetime, // objectFactory: this.objectFactory, parent: this, timeout: this.timeout, adjustTransportLimits: this.transportSettings?.adjustTransportLimits }); debugLog("channel Timeout = >", channel.timeout); socket.resume(); this._preregisterChannel(channel); channel.init(socket, (err) => { this._un_pre_registerChannel(channel); debugLog(chalk_1.default.yellow.bold("Channel#init done"), err); if (err) { const reason = "openSecureChannel has Failed " + err.message; const socketData = extractSocketData(socket, reason); const channelData = extractChannelData(channel); this.emit("openSecureChannelFailure", socketData, channelData); socket.end(); socket.destroy(); } else { debugLog("server receiving a client connection"); this._registerChannel(channel); } }); channel.on("message", (message) => { // forward this.emit("message", message, channel, this); }); }; // Each SecureChannel exists until it is explicitly closed or until the last token has expired and the overlap // period has elapsed. A Server application should limit the number of SecureChannels. // To protect against misbehaving Clients and denial of service attacks, the Server shall close the oldest // SecureChannel that has no Session assigned before reaching the maximum number of supported SecureChannels. this._prevent_DDOS_Attack(establish_connection, deny_connection); } _preregisterChannel(channel) { // _preregisterChannel is used to keep track of channel for which // that are in early stage of the hand shaking process. // e.g HEL/ACK and OpenSecureChannel may not have been received yet // as they will need to be interrupted when OPCUAServerEndPoint is closed (0, node_opcua_assert_1.assert)(this._started, "OPCUAServerEndPoint must be started"); (0, node_opcua_assert_1.assert)(!Object.prototype.hasOwnProperty.call(this._channels, channel.hashKey), " channel already preregistered!"); const channelPriv = channel; this._channels[channel.hashKey] = channelPriv; channelPriv._unpreregisterChannelEvent = () => { debugLog("Channel received an abort event during the preregistration phase"); this._un_pre_registerChannel(channel); channel.dispose(); }; channel.on("abort", channelPriv._unpreregisterChannelEvent); } _un_pre_registerChannel(channel) { if (!this._channels[channel.hashKey]) { debugLog("Already un preregistered ?", channel.hashKey); return; } delete this._channels[channel.hashKey]; const channelPriv = channel; if (typeof channelPriv._unpreregisterChannelEvent === "function") { channel.removeListener("abort", channelPriv._unpreregisterChannelEvent); channelPriv._unpreregisterChannelEvent = undefined; } } /** * @private */ _registerChannel(channel) { if (this._started) { debugLog(chalk_1.default.red("_registerChannel = "), "channel.hashKey = ", channel.hashKey); (0, node_opcua_assert_1.assert)(!this._channels[channel.hashKey]); this._channels[channel.hashKey] = channel; /** * @event newChannel * @param channel */ this.emit("newChannel", channel); channel.on("abort", () => { this._unregisterChannel(channel); }); } else { debugLog("OPCUAServerEndPoint#_registerChannel called when end point is shutdown !"); debugLog(" -> channel will be forcefully terminated"); channel.close(() => { channel.dispose(); }); } } /** */ _unregisterChannel(channel) { debugLog("_un-registerChannel channel.hashKey", channel.hashKey); if (!Object.prototype.hasOwnProperty.call(this._channels, channel.hashKey)) { return; } (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(this._channels, channel.hashKey), "channel is not registered"); /** * @event closeChannel * @param channel */ this.emit("closeChannel", channel); // keep trace of statistics data from old channel for our own accumulated stats. this.bytesWrittenInOldChannels += channel.bytesWritten; this.bytesReadInOldChannels += channel.bytesRead; this.transactionsCountOldChannels += channel.transactionsCount; delete this._channels[channel.hashKey]; // istanbul ignore next if (doDebug) { this._dump_statistics(); debugLog("un-registering channel - Count = ", this.currentChannelCount); } /// channel.dispose(); } _end_listen(err) { if (!this._listen_callback) return; (0, node_opcua_assert_1.assert)(typeof this._listen_callback === "function"); this._listen_callback(err); this._listen_callback = undefined; } /** * shutdown_channel * @param channel * @param inner_callback */ shutdown_channel(channel, inner_callback) { (0, node_opcua_assert_1.assert)(typeof inner_callback === "function"); channel.once("close", () => { // xx console.log(" ON CLOSED !!!!"); }); channel.close(() => { this._unregisterChannel(channel); setImmediate(inner_callback); }); } /** * @private */ _prevent_DDOS_Attack(establish_connection, deny_connection) { const nbConnections = this.activeChannelCount; if (nbConnections >= this.maxConnections) { // istanbul ignore next errorLog(chalk_1.default.bgRed.white("PREVENTING DDOS ATTACK => maxConnection =" + this.maxConnections)); const unused_channels = this.getChannels().filter((channel1) => { return !channel1.hasSession; }); if (unused_channels.length === 0) { doDebug && console.log(this.getChannels() .map(({ status, isOpened, hasSession }) => `${status} ${isOpened} ${hasSession}\n`) .join(" ")); // all channels are in used , we cannot get any errorLog(`All channels are in used ! we cannot cancel any ${this.getChannels().length}`); // istanbul ignore next if (doDebug) { console.log(" - all channels are used !!!!"); false && dumpChannelInfo(this.getChannels()); } setTimeout(deny_connection, 1000); return; } // istanbul ignore next if (doDebug) { console.log(" - Unused channels that can be clobbered", unused_channels.map((channel1) => channel1.hashKey).join(" ")); } const channel = unused_channels[0]; errorLog(`${unused_channels.length} : Forcefully closing oldest channel that have no session: ${channel.hashKey}`); channel.close(() => { // istanbul ignore next if (doDebug) { console.log(" _ Unused channel has been closed ", channel.hashKey); } this._unregisterChannel(channel); establish_connection(); }); } else { setImmediate(establish_connection); } } } exports.OPCUAServerEndPoint = OPCUAServerEndPoint; function estimateSecurityLevel(securityMode, securityPolicy) { if (securityMode === node_opcua_secure_channel_1.MessageSecurityMode.None) { return 1; } let offset = 100; if (securityMode === node_opcua_secure_channel_1.MessageSecurityMode.SignAndEncrypt) { offset = 200; } switch (securityPolicy) { case node_opcua_secure_channel_1.SecurityPolicy.Basic128: case node_opcua_secure_channel_1.SecurityPolicy.Basic128Rsa15: case node_opcua_secure_channel_1.SecurityPolicy.Basic192: return 2; // deprecated => low case node_opcua_secure_channel_1.SecurityPolicy.Basic192Rsa15: return 3; // deprecated => low case node_opcua_secure_channel_1.SecurityPolicy.Basic256: return 4; // deprecated => low case node_opcua_secure_channel_1.SecurityPolicy.Basic256Rsa15: return 4 + offset; case node_opcua_secure_channel_1.SecurityPolicy.Aes128_Sha256_RsaOaep: return 5 + offset; case node_opcua_secure_channel_1.SecurityPolicy.Basic256Sha256: return 6 + offset; case node_opcua_secure_channel_1.SecurityPolicy.Aes256_Sha256_RsaPss: return 7 + offset; default: case node_opcua_secure_channel_1.SecurityPolicy.None: return 1; } } /** * @private */ function _makeEndpointDescription(options, parent) { (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(options, "serverCertificateChain")); (0, node_opcua_assert_1.assert)(!Object.prototype.hasOwnProperty.call(options, "serverCertificate")); (0, node_opcua_assert_1.assert)(!!options.securityMode); // s.MessageSecurityMode (0, node_opcua_assert_1.assert)(!!options.securityPolicy); (0, node_opcua_assert_1.assert)(options.server !== null && typeof options.server === "object"); (0, node_opcua_assert_1.assert)(!!options.hostname && typeof options.hostname === "string"); (0, node_opcua_assert_1.assert)(typeof options.restricted === "boolean"); const u = (n) => getUniqueName(n, options.collection); options.securityLevel = options.securityLevel === undefined ? estimateSecurityLevel(options.securityMode, options.securityPolicy) : options.securityLevel; (0, node_opcua_assert_1.assert)(isFinite(options.securityLevel), "expecting a valid securityLevel"); const securityPolicyUri = (0, node_opcua_secure_channel_1.toURI)(options.securityPolicy); const userIdentityTokens = []; const registerIdentity2 = (tokenType, securityPolicy, name) => { return registerIdentity({ policyId: u(name), tokenType, issuedTokenType: null, issuerEndpointUrl: null, securityPolicyUri: securityPolicy }); }; const registerIdentity = (r) => { const tokenType = r.tokenType === undefined ? node_opcua_service_endpoints_1.UserTokenType.Invalid : r.tokenType; const securityPolicy = (r.securityPolicyUri || ""); if (!securityPolicy && options.userTokenTypes.indexOf(tokenType) >= 0) { userIdentityTokens.push(r); return; } if (options.securityPolicies.indexOf(securityPolicy) >= 0 && options.userTokenTypes.indexOf(tokenType) >= 0) { userIdentityTokens.push(r); } }; if (!options.noUserIdentityTokens) { if (options.securityPolicy === node_opcua_secure_channel_1.SecurityPolicy.None) { if (options.allowUnsecurePassword) { registerIdentity({ policyId: u("username_unsecure"), tokenType: node_opcua_service_endpoints_1.UserTokenType.UserName, issuedTokenType: null, issuerEndpointUrl: null, securityPolicyUri: null }); } const onlyCertificateLessConnection = options.onlyCertificateLessConnection === undefined ? false : options.onlyCertificateLessConnection; if (!onlyCertificateLessConnection) { registerIdentity2(node_opcua_service_endpoints_1.UserTokenType.UserName, node_opcua_secure_channel_1.SecurityPolicy.Basic256, "username_basic256"); registerIdentity2(node_opcua_service_endpoints_1.UserTokenType.UserName, node_opcua_secure_channel_1.SecurityPolicy.Basic128Rsa15, "username_basic128Rsa15"); registerIdentity2(node_opcua_service_endpoints_1.UserTokenType.UserName, node_opcua_secure_channel_1.SecurityPolicy.Basic256Sha256, "username_basic256Sha256"); registerIdentity2(node_opcua_service_endpoints_1.UserTokenType.UserName, node_opcua_secure_channel_1.SecurityPolicy.Aes128_Sha256_RsaOaep, "username_aes128Sha256RsaOaep"); // X509 registerIdentity2(node_opcua_service_endpoints_1.UserTokenType.Certificate, node_opcua_secure_channel_1.SecurityPolicy.Basic256, "certificate_basic256"); registerIdentity2(node_opcua_service_endpoints_1.UserTokenType.Certificate, node_opcua_secure_channel_1.SecurityPolicy.Basic128Rsa15, "certificate_basic128Rsa15"); registerIdentity2(node_opcua_service_endpoints_1.UserTokenType.Certificate, node_opcua_secure_channel_1.SecurityPolicy.Basic256Sha256, "certificate_basic256Sha256"); registerIdentity2(node_opcua_service_endpoints_1.UserTokenType.Certificate, node_opcua_secure_channel_1.SecurityPolicy.Aes128_Sha256_RsaOaep, "certificate_aes128Sha256RsaOaep"); } } else { // note: // when channel session security is not "None", // userIdentityTokens can be left to null. // in this case this mean that secure policy will be the same as connection security policy // istanbul ignore next if (process.env.NODEOPCUA_SERVER_EMULATE_SIEMENS) { // However, for some reason SIEMENS plc requires that password get encrypted even though // the secure channel is also encrypted .... // you can set the NODEOPCUA_SERVER_EMULATE_SIEMENS env variable to simulate this behavior const registerIdentity3 = (tokenType, securityPolicy, name) => { const identity = { policyId: u(name), tokenType, issuedTokenType: null, issuerEndpointUrl: null, securityPolicyUri: securityPolicy }; userIdentityTokens.push(identity); }; registerIdentity3(node_opcua_service_endpoints_1.UserTokenType.UserName, node_opcua_secure_channel_1.SecurityPolicy.Basic256, "username_basic256"); registerIdentity3(node_opcua_service_endpoints_1.UserTokenType.UserName, node_opcua_secure_channel_1.SecurityPolicy.Basic128Rsa15, "username_basic128Rsa15"); registerIdentity3(node_opcua_service_endpoints_1.UserTokenType.UserName, node_opcua_secure_channel_1.SecurityPolicy.Basic256Sha256, "username_basic256Sha256"); } else { registerIdentity({ policyId: u("usernamePassword"), tokenType: node_opcua_service_endpoints_1.UserTokenType.UserName, issuedTokenType: null, issuerEndpointUrl: null, securityPolicyUri: null }); registerIdentity({ policyId: u("certificateX509"), tokenType: node_opcua_service_endpoints_1.UserTokenType.Certificate, issuedTokenType: null, issuerEndpointUrl: null, securityPolicyUri: null }); } } registerIdentity({ policyId: u("anonymous"), tokenType: node_opcua_service_endpoints_1.UserTokenType.Anonymous, issuedTokenType: null, issuerEndpointUrl: null, securityPolicyUri: null }); } // return the endpoint object const endpoint = new node_opcua_service_endpoints_2.EndpointDescription({ endpointUrl: "<to be evaluated at run time>", // options.endpointUrl, server: undefined, // options.server, serverCertificate: options.serverCertificateChain, securityMode: options.securityMode, securityPolicyUri, userIdentityTokens, securityLevel: options.securityLevel, transportProfileUri: default_transportProfileUri }); endpoint._parent = parent; // endpointUrl is dynamic as port number may be adjusted // when the tcp socket start listening endpoint.__defineGetter__("endpointUrl", () => { const port = endpoint._parent.port; const resourcePath = options.resourcePath || ""; const hostname = options.hostname; const endpointUrl = `opc.tcp://${hostname}:${port}${resourcePath}`; return (0, node_opcua_hostname_1.resolveFullyQualifiedDomainName)(endpointUrl); }); endpoint.server = options.server; endpoint.restricted = options.restricted; return endpoint; } /** * return true if the end point matches security mode and policy * @param endpoint * @param securityMode * @param securityPolicy * @internal * */ function matching_endpoint(securityMode, securityPolicy, endpointUrl, endpoint) { (0, node_opcua_assert_1.assert)(endpoint instanceof node_opcua_service_endpoints_2.EndpointDescription); const endpoint_securityPolicy = (0, node_opcua_secure_channel_1.fromURI)(endpoint.securityPolicyUri); if (endpointUrl && endpoint.endpointUrl !== endpointUrl) { return false; } return endpoint.securityMode === securityMode && endpoint_securityPolicy === securityPolicy; } const defaultSecurityModes = [node_opcua_secure_channel_1.MessageSecurityMode.None, node_opcua_secure_channel_1.MessageSecurityMode.Sign, node_opcua_secure_channel_1.MessageSecurityMode.SignAndEncrypt]; const defaultSecurityPolicies = [ // now deprecated Basic128Rs15 shall be disabled by default // see https://profiles.opcfoundation.org/profile/1532 // SecurityPolicy.Basic128Rsa15, // now deprecated Basic256 shall be disabled by default // see https://profiles.opcfoundation.org/profile/2062 // SecurityPolicy.Basic256, // xx UNUSED!! SecurityPolicy.Basic192Rsa15, // xx UNUSED!! SecurityPolicy.Basic256Rsa15, node_opcua_secure_channel_1.SecurityPolicy.Basic256Sha256, node_opcua_secure_channel_1.SecurityPolicy.Aes128_Sha256_RsaOaep, node_opcua_secure_channel_1.SecurityPolicy.Aes256_Sha256_RsaPss ]; const defaultUserTokenTypes = [ node_opcua_service_endpoints_1.UserTokenType.Anonymous, node_opcua_service_endpoints_1.UserTokenType.UserName, node_opcua_service_endpoints_1.UserTokenType.Certificate // NOT USED YET : UserTokenType.IssuedToken ]; //# sourceMappingURL=server_end_point.js.map