node-red-contrib-tdn-eventsub 0.2.1 → 0.2.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/README.md +1 -0
- package/examples/rest-basic.json +1 -0
- package/lib/client.js +15 -1
- package/nodes/rest-server.html +21 -1
- package/nodes/rest-server.js +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,6 +39,7 @@ Restart Node-RED. Nodes appear under **TDN REST**. Import the example via *Impor
|
|
|
39
39
|
| Auth scheme | Authorization header only. Blank (default): `Authorization: Basic base64(":" + key)`, accepted by server v9.0+. For older servers enter the API-key scheme token from your server's REST API docs; the key is then sent as `Authorization: <scheme> <key>`. |
|
|
40
40
|
| Header name | API key header only. Sent as `<header name>: <key>`; default `X-API-Key`. |
|
|
41
41
|
| Cert fingerprint | Optional. SHA-256 (64 hex) or SHA-1 (40 hex) of the **server** certificate, colons optional. When set, the server's self-signed cert is accepted only if it matches; the check runs before any request is written, so the key is never sent to an impostor. A mismatch error prints the actual fingerprint. |
|
|
42
|
+
| Plain HTTP (insecure) | Unencrypted `http://` whatever the URL says; certificate settings are ignored and the key travels in clear text. For bench testing against a non-TLS endpoint only. A TLS error like `unexpected message` or `wrong version number` usually means the endpoint needs this; the error text says so. |
|
|
42
43
|
| Verify server certificate | Normal CA check when no fingerprint is set. |
|
|
43
44
|
| Force links onto this host:port | The server builds `href`s from its own machine name. With this on, followed links keep path + query but use your configured host/port. With it off, a link that would downgrade `https` to `http` is refused. |
|
|
44
45
|
| Client cert / key / PFX | Only if the REST Client item on the server has a thumbprint (pinned client certificate). |
|
package/examples/rest-basic.json
CHANGED
package/lib/client.js
CHANGED
|
@@ -84,6 +84,18 @@ function buildAuthHeaders(cred) {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// OpenSSL errors seen when the other end isn't speaking TLS at all
|
|
88
|
+
const NOT_TLS_CODES = ['ERR_SSL_WRONG_VERSION_NUMBER', 'ERR_SSL_UNEXPECTED_MESSAGE', 'ERR_SSL_PACKET_LENGTH_TOO_LONG', 'ERR_SSL_HTTP_REQUEST', 'ERR_SSL_RECORD_LAYER_FAILURE'];
|
|
89
|
+
|
|
90
|
+
/** Add a hint to TLS handshake errors that usually mean the endpoint is plain HTTP. */
|
|
91
|
+
function notTlsHint(e) {
|
|
92
|
+
if (e && NOT_TLS_CODES.includes(e.code)) {
|
|
93
|
+
e.message = `TLS handshake failed (${e.code}): the endpoint may not be using TLS. ` +
|
|
94
|
+
'For a plain-HTTP endpoint tick "Plain HTTP (insecure)" in the server config. ' + e.message;
|
|
95
|
+
}
|
|
96
|
+
return e;
|
|
97
|
+
}
|
|
98
|
+
|
|
87
99
|
function abortError() {
|
|
88
100
|
return new RestError('Request aborted', { code: 'ABORTED', aborted: true });
|
|
89
101
|
}
|
|
@@ -101,6 +113,7 @@ class RestClient {
|
|
|
101
113
|
* @param {boolean} [o.rejectUnauthorized] used only when no fingerprint is set
|
|
102
114
|
* @param {string} [o.certPath] [o.keyPath] [o.pfxPath] [o.passphrase] client certificate
|
|
103
115
|
* @param {boolean} [o.rewriteHrefs] force followed hrefs back onto the configured host:port
|
|
116
|
+
* @param {boolean} [o.plainHttp] use unencrypted http:// whatever the URL says (bench/test only)
|
|
104
117
|
* @param {number} [o.timeoutMs] default request timeout
|
|
105
118
|
*/
|
|
106
119
|
constructor(o) {
|
|
@@ -109,6 +122,7 @@ class RestClient {
|
|
|
109
122
|
if (!raw) throw new RestError('No server URL configured');
|
|
110
123
|
if (!/^https?:\/\//i.test(raw)) raw = 'https://' + raw;
|
|
111
124
|
const u = new URL(raw);
|
|
125
|
+
if (o.plainHttp) u.protocol = 'http:';
|
|
112
126
|
const port = parseInt(o.port, 10);
|
|
113
127
|
if (port > 0) u.port = String(port);
|
|
114
128
|
else if (!u.port) u.port = String(DEFAULT_PORT);
|
|
@@ -230,7 +244,7 @@ class RestClient {
|
|
|
230
244
|
if (!net.isIP(host)) opts.servername = host;
|
|
231
245
|
|
|
232
246
|
sock = tls.connect(opts);
|
|
233
|
-
sock.once('error', (e) => finish(e));
|
|
247
|
+
sock.once('error', (e) => finish(notTlsHint(e)));
|
|
234
248
|
sock.once('secureConnect', () => {
|
|
235
249
|
if (pinning) {
|
|
236
250
|
const c = sock.getPeerCertificate();
|
package/nodes/rest-server.html
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
return h.length === 40 || h.length === 64;
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
|
+
plainHttp: { value: false },
|
|
19
20
|
rejectUnauthorized: { value: true },
|
|
20
21
|
rewriteHrefs: { value: true },
|
|
21
22
|
timeout: { value: 30000, validate: RED.validators.number() },
|
|
@@ -39,6 +40,13 @@
|
|
|
39
40
|
if (!this.authType) $('#node-config-input-authType').val('authorization');
|
|
40
41
|
$('#node-config-input-authType').on('change', sync);
|
|
41
42
|
sync();
|
|
43
|
+
var syncTls = function () {
|
|
44
|
+
var plain = $('#node-config-input-plainHttp').is(':checked');
|
|
45
|
+
$('.tdn-rest-tls').toggle(!plain);
|
|
46
|
+
$('.tdn-rest-plain-warning').toggle(plain);
|
|
47
|
+
};
|
|
48
|
+
$('#node-config-input-plainHttp').on('change', syncTls);
|
|
49
|
+
syncTls();
|
|
42
50
|
}
|
|
43
51
|
});
|
|
44
52
|
</script>
|
|
@@ -76,10 +84,16 @@
|
|
|
76
84
|
<input type="text" id="node-config-input-headerName" placeholder="X-API-Key">
|
|
77
85
|
</div>
|
|
78
86
|
<div class="form-row">
|
|
87
|
+
<label> </label>
|
|
88
|
+
<input type="checkbox" id="node-config-input-plainHttp" style="display:inline-block;width:auto;vertical-align:top">
|
|
89
|
+
<label for="node-config-input-plainHttp" style="width:auto">Plain HTTP (insecure, no TLS)</label>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="form-tips tdn-rest-plain-warning"><b>Insecure:</b> the API key and all data are sent unencrypted. Use only for bench testing against a non-TLS endpoint.</div>
|
|
92
|
+
<div class="form-row tdn-rest-tls">
|
|
79
93
|
<label for="node-config-input-fingerprint"><i class="fa fa-certificate"></i> Cert fingerprint</label>
|
|
80
94
|
<input type="text" id="node-config-input-fingerprint" placeholder="optional SHA-256 or SHA-1 of server cert">
|
|
81
95
|
</div>
|
|
82
|
-
<div class="form-row">
|
|
96
|
+
<div class="form-row tdn-rest-tls">
|
|
83
97
|
<label> </label>
|
|
84
98
|
<input type="checkbox" id="node-config-input-rejectUnauthorized" style="display:inline-block;width:auto;vertical-align:top">
|
|
85
99
|
<label for="node-config-input-rejectUnauthorized" style="width:auto">Verify server certificate against CA (ignored when a fingerprint is set)</label>
|
|
@@ -93,6 +107,7 @@
|
|
|
93
107
|
<label for="node-config-input-timeout"><i class="fa fa-clock-o"></i> Timeout (ms)</label>
|
|
94
108
|
<input type="text" id="node-config-input-timeout" placeholder="30000" style="width:100px">
|
|
95
109
|
</div>
|
|
110
|
+
<div class="tdn-rest-tls">
|
|
96
111
|
<hr>
|
|
97
112
|
<div class="form-tips">Client certificate — only if the REST Client item on the server has a thumbprint set. Use PEM cert + key, <i>or</i> a PFX.</div>
|
|
98
113
|
<div class="form-row">
|
|
@@ -111,6 +126,7 @@
|
|
|
111
126
|
<label for="node-config-input-passphrase">Passphrase</label>
|
|
112
127
|
<input type="password" id="node-config-input-passphrase">
|
|
113
128
|
</div>
|
|
129
|
+
</div>
|
|
114
130
|
</script>
|
|
115
131
|
|
|
116
132
|
<script type="text/html" data-help-name="tdn-rest-server">
|
|
@@ -138,6 +154,10 @@
|
|
|
138
154
|
When set, the connection is accepted only if the presented certificate matches, even if it is self-signed.
|
|
139
155
|
The check happens before any request (and therefore the API key) is sent.
|
|
140
156
|
A mismatch error reports the actual fingerprint so you can copy it.</dd>
|
|
157
|
+
<dt>Plain HTTP (insecure)</dt>
|
|
158
|
+
<dd>Connect with unencrypted <code>http://</code> whatever the URL says. The API key travels in clear text and the
|
|
159
|
+
certificate settings are ignored, so use it only for bench testing against a non-TLS endpoint. A TLS handshake error such as
|
|
160
|
+
<code>unexpected message</code> or <code>wrong version number</code> usually means the endpoint needs this.</dd>
|
|
141
161
|
<dt>Verify server certificate</dt>
|
|
142
162
|
<dd>Normal CA verification when no fingerprint is set. Untick only for lab use.</dd>
|
|
143
163
|
<dt>Force returned links back onto this host:port</dt>
|
package/nodes/rest-server.js
CHANGED
|
@@ -16,6 +16,7 @@ module.exports = function (RED) {
|
|
|
16
16
|
this.headerName = (config.headerName || '').trim();
|
|
17
17
|
this.rejectUnauthorized = config.rejectUnauthorized !== false;
|
|
18
18
|
this.rewriteHrefs = config.rewriteHrefs !== false;
|
|
19
|
+
this.plainHttp = config.plainHttp === true;
|
|
19
20
|
this.timeout = parseInt(config.timeout, 10) || 30000;
|
|
20
21
|
this.certPath = config.certPath;
|
|
21
22
|
this.keyPath = config.keyPath;
|
|
@@ -36,13 +37,17 @@ module.exports = function (RED) {
|
|
|
36
37
|
fingerprint: this.fingerprint,
|
|
37
38
|
rejectUnauthorized: this.rejectUnauthorized,
|
|
38
39
|
rewriteHrefs: this.rewriteHrefs,
|
|
40
|
+
plainHttp: this.plainHttp,
|
|
39
41
|
timeoutMs: this.timeout,
|
|
40
42
|
certPath: this.certPath,
|
|
41
43
|
keyPath: this.keyPath,
|
|
42
44
|
pfxPath: this.pfxPath,
|
|
43
45
|
passphrase: creds.passphrase
|
|
44
46
|
});
|
|
45
|
-
if (this.client.base.protocol === 'http:')
|
|
47
|
+
if (this.client.base.protocol === 'http:') {
|
|
48
|
+
this.warn('REST server uses plain HTTP - the API key will be sent unencrypted' +
|
|
49
|
+
(this.fingerprint || this.certPath || this.pfxPath ? '; certificate settings are ignored' : ''));
|
|
50
|
+
}
|
|
46
51
|
} catch (e) {
|
|
47
52
|
this.clientError = e;
|
|
48
53
|
this.error('REST server config: ' + e.message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-tdn-eventsub",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Node-RED nodes for an access-control REST Server API: access groups, cardholders and items lists to global context, plus continuous item status subscriptions.",
|
|
5
5
|
"main": "lib/client.js",
|
|
6
6
|
"scripts": {
|