Skip to content

Commit

Permalink
Merge pull request #44 from boschrexroth/bugfix/ipv6-on-linux
Browse files Browse the repository at this point in the history
Bugfix/crash on browsing for bad credentials
  • Loading branch information
Sebastian Krauskopf committed May 12, 2022
2 parents d91816f + baa2c33 commit 05f05b3
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 188 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -97,7 +97,8 @@ Any use of the source code and related documents of this repository in applicati
* 2022-03-14: 1.8.15 - fix: make subscribe node useable on other ports than 443 (Bug457112).
fix: remove an uncaught exception which was introduced with version 1.8.14 (Bug454078).
* 2022-04-26: 1.8.16 - fix: possible connection break on heavy load for commands: create, delete, write.
* 2022-05-28: 1.8.17 - feat: added support for IPv6.
* 2022-05-05: 1.8.17 - feat: added support for IPv6.
* 2022-05-06: 1.8.18 - fix: possible node crash on browsing with bad credentials

## About

Expand Down
2 changes: 1 addition & 1 deletion ctrlx-config-subscription.js
Expand Up @@ -24,7 +24,7 @@
*
*/

const CtrlxProblemError = require('./lib/CtrlxProblemError');
const CtrlxProblemError = require('./lib/CtrlxProblemError');

module.exports = function(RED) {
'use strict';
Expand Down
205 changes: 104 additions & 101 deletions ctrlx-config.js
Expand Up @@ -38,8 +38,8 @@ module.exports = function(RED) {

// https://discourse.nodered.org/t/create-an-admin-configuration-api-https-endpoint/4423/7
// https://discourse.nodered.org/t/accessing-server-side-from-client-side/26022/4
RED.httpAdmin.get('/ctrlx/browse', function(req, res/*, next*/) {
//console.log(req.query);
RED.httpAdmin.get('/ctrlx/browse', function(req, res) {
// console.log(req.query);

const id = req.query.id;
const username = req.query.username;
Expand All @@ -54,9 +54,9 @@ module.exports = function(RED) {
let ctrlx = new CtrlxCore(hostname, username, password);

ctrlx.logIn()
.then(() => ctrlx.datalayerBrowse(path) )
.then(() => ctrlx.datalayerBrowse(path))
.then((data) => {
if (!data || !data.value ) {
if (!data || !data.value) {
return res.end('[]');
}
res.end(JSON.stringify(data.value));
Expand All @@ -70,7 +70,10 @@ module.exports = function(RED) {
return res.end(err.message);
}
})
.finally(() => ctrlx.logOut());
.finally(() => {
// We have to catch, because this fails for bad credentials
ctrlx.logOut().catch((err) => RED.log.warn('Failed to log out when trying to browse with error ' + err.message));
});

} else if (id) {

Expand All @@ -93,7 +96,7 @@ module.exports = function(RED) {
return res.end(err.message);
}

if (!data || !data.value ) {
if (!data || !data.value) {
return res.end('[]');
}

Expand Down Expand Up @@ -150,127 +153,127 @@ module.exports = function(RED) {
this.register = function(ctrlxNode) {
node.users[ctrlxNode.id] = ctrlxNode;
if (Object.keys(node.users).length === 1) {
node.connect();
node.connect();
}
};

// Unregister of attached ctrlX node. We log out of ctrlX when the last node unregistered.
this.deregister = function(ctrlxNode, done) {
delete node.users[ctrlxNode.id];
if (node.closing) {
return done();
}
if (Object.keys(node.users).length === 0) {
if (node.ctrlX) {
node.ctrlX.logOut()
.then(() => done())
.catch(() => done());
}
} else {
done();
delete node.users[ctrlxNode.id];
if (node.closing) {
return done();
}
if (Object.keys(node.users).length === 0) {
if (node.ctrlX) {
node.ctrlX.logOut()
.then(() => done())
.catch(() => done());
}
} else {
done();
}
};

// This function performs the login. Will be automatically called as soon as
// the first node registers.
this.connect = function () {
this.connect = function() {
if (!node.connected && !node.connecting) {
node.connecting = true;
try {
node.ctrlX.logIn()
.then((data) => {
node.connecting = false;
node.connected = true;

if (node.debug) {
node.log('Successfully logged in to: ' + node.hostname);
node.log('Token will expire at ' + new Date(data.token_expireTime).toLocaleString() + ' local time');
}

for (let id in node.users) {
if (Object.prototype.hasOwnProperty.call(node.users, id)) {
node.users[id].setStatus({fill: 'green', shape: 'dot', text: 'authenticated'});
.then((data) => {
node.connecting = false;
node.connected = true;

if (node.debug) {
node.log('Successfully logged in to: ' + node.hostname);
node.log('Token will expire at ' + new Date(data.token_expireTime).toLocaleString() + ' local time');
}
}

// Now execute all the pending requests
for (let id in node.pendingRequests) {
if (Object.prototype.hasOwnProperty.call(node.pendingRequests, id)) {
for (let id in node.users) {
if (Object.prototype.hasOwnProperty.call(node.users, id)) {
node.users[id].setStatus({ fill: 'green', shape: 'dot', text: 'authenticated' });
}
}

switch(node.pendingRequests[id].method) {
case 'READ': {
node.datalayerRead(id, node.pendingRequests[id].path, node.pendingRequests[id].callback);
break;
}
case 'READ_WITH_ARG': {
node.datalayerReadWithArg(id, node.pendingRequests[id].path, node.pendingRequests[id].arg, node.pendingRequests[id].callback);
break;
}
case 'WRITE': {
node.datalayerWrite(id, node.pendingRequests[id].path, node.pendingRequests[id].data, node.pendingRequests[id].callback);
break;
}
case 'CREATE': {
node.datalayerCreate(id, node.pendingRequests[id].path, node.pendingRequests[id].data, node.pendingRequests[id].callback);
break;
}
case 'DELETE': {
node.datalayerDelete(id, node.pendingRequests[id].path, node.pendingRequests[id].callback);
break;
}
case 'METADATA': {
node.datalayerReadMetadata(id, node.pendingRequests[id].path, node.pendingRequests[id].callback);
break;
// Now execute all the pending requests
for (let id in node.pendingRequests) {
if (Object.prototype.hasOwnProperty.call(node.pendingRequests, id)) {

switch (node.pendingRequests[id].method) {
case 'READ': {
node.datalayerRead(id, node.pendingRequests[id].path, node.pendingRequests[id].callback);
break;
}
case 'READ_WITH_ARG': {
node.datalayerReadWithArg(id, node.pendingRequests[id].path, node.pendingRequests[id].arg, node.pendingRequests[id].callback);
break;
}
case 'WRITE': {
node.datalayerWrite(id, node.pendingRequests[id].path, node.pendingRequests[id].data, node.pendingRequests[id].callback);
break;
}
case 'CREATE': {
node.datalayerCreate(id, node.pendingRequests[id].path, node.pendingRequests[id].data, node.pendingRequests[id].callback);
break;
}
case 'DELETE': {
node.datalayerDelete(id, node.pendingRequests[id].path, node.pendingRequests[id].callback);
break;
}
case 'METADATA': {
node.datalayerReadMetadata(id, node.pendingRequests[id].path, node.pendingRequests[id].callback);
break;
}
case 'BROWSE': {
node.datalayerBrowse(id, node.pendingRequests[id].path, node.pendingRequests[id].callback);
break;
}
case 'SUBSCRIBE': {
node.datalayerSubscribe(id, node.pendingRequests[id].paths, node.pendingRequests[id].publishIntervalMs, node.pendingRequests[id].callback);
break;
}
default: {
node.error('internal error: received invalid pending request!');
}
}
case 'BROWSE': {
node.datalayerBrowse(id, node.pendingRequests[id].path, node.pendingRequests[id].callback);
break;
}
case 'SUBSCRIBE': {
node.datalayerSubscribe(id, node.pendingRequests[id].paths, node.pendingRequests[id].publishIntervalMs, node.pendingRequests[id].callback);
break;
}
default: {
node.error('internal error: received invalid pending request!');
}
}

delete node.pendingRequests[id];
delete node.pendingRequests[id];
}
}
}

})
.catch((err) => {
node.connecting = false;
node.connected = false;
})
.catch((err) => {
node.connecting = false;
node.connected = false;

if (node.debug) {
node.log('Failed to log in to ' + node.hostname + ' with error ' + err.message);
}
if (node.debug) {
node.log('Failed to log in to ' + node.hostname + ' with error ' + err.message);
}

for (let id in node.users) {
if (Object.prototype.hasOwnProperty.call(node.users, id)) {
node.users[id].setStatus({fill: 'red', shape: 'ring', text: 'authentication failed'});
for (let id in node.users) {
if (Object.prototype.hasOwnProperty.call(node.users, id)) {
node.users[id].setStatus({ fill: 'red', shape: 'ring', text: 'authentication failed' });
}
}
}

// Now cancel all the pending requests
for (let id in node.pendingRequests) {
if (Object.prototype.hasOwnProperty.call(node.pendingRequests, id)) {
node.pendingRequests[id].callback(err, null);
delete node.pendingRequests[id];
// Now cancel all the pending requests
for (let id in node.pendingRequests) {
if (Object.prototype.hasOwnProperty.call(node.pendingRequests, id)) {
node.pendingRequests[id].callback(err, null);
delete node.pendingRequests[id];
}
}
}

// Try again, except if the node has been closed in the meanwhile. E.g. because
// the node has been deleted or the flow has been reployed with new settings.
if (!node.closing) {
setTimeout(node.connect, 5000);
}
// Try again, except if the node has been closed in the meanwhile. E.g. because
// the node has been deleted or the flow has been reployed with new settings.
if (!node.closing) {
setTimeout(node.connect, 5000);
}

});
});

}catch(err) {
} catch (err) {
node.error(err);
}
}
Expand Down Expand Up @@ -440,8 +443,8 @@ module.exports = function(RED) {

RED.nodes.registerType("ctrlx-config", CtrlxConfig, {
credentials: {
username: {type:"text"},
password: {type:"password"}
}
username: { type: "text" },
password: { type: "password" }
}
});
};

0 comments on commit 05f05b3

Please sign in to comment.