|
[ |
|
{ |
|
"id": "a09b9281644a88fc", |
|
"type": "comment", |
|
"z": "d8fe099880fd703d", |
|
"name": "Cookie-based authorization (w/ expiration)", |
|
"info": "", |
|
"x": 180, |
|
"y": 40, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "07b8b379eee00444", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "validate authorization", |
|
"func": " let Cookie = ((msg.req.cookies || {}).authorization || '').trim()\n if (Cookie !== '') {\n let UserRegistry = global.get('UserRegistry') || Object.create(null)\n\n let [UserId,Expiration,Digest] = Cookie.split(':')\n UserId = UserId.toLowerCase()\n if (\n (UserId !== '') && (UserId in UserRegistry) && (UserRegistry[UserId] != null) &&\n /^\\d+$/.test(Expiration) && /^[0-9a-fA-F]+$/.test(Digest)\n ) {\n let TokenKey = global.get('TokenKey')\n const HMAC = crypto.createHmac('sha256',TokenKey)\n HMAC.update(UserId + ':' + Expiration)\n let expectedDigest = HMAC.digest('hex')\n\n if (\n (Digest === expectedDigest) &&\n (parseInt(Expiration,10) >= Date.now())\n ) {\n let UserRoles = UserRegistry[UserId].Roles || []\n if (\n (msg.requiredRole == null) ||\n (UserRoles.indexOf(msg.requiredRole) >= 0)\n ) {\n msg.authenticatedUser = UserId\n msg.authorizedRoles = UserRegistry[UserId].Roles || []\n\n msg.cookies = msg.cookies || {}\n msg.cookies.authorization = Cookie\n return [msg,null] // authorized\n } else {\n msg.cookies = msg.cookies || {}\n msg.cookies.authorization = null\n\n msg.payload = 'Unauthorized'\n msg.statusCode = 401\n return [null,msg] // not authorized\n }\n }\n }\n }\n\n msg.cookies = msg.cookies || {}\n msg.cookies.authorization = null\n\n msg.payload = 'Unauthorized'\n msg.statusCode = 401\n return [null,msg] // not authorized\n", |
|
"outputs": 2, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [ |
|
{ |
|
"var": "crypto", |
|
"module": "crypto" |
|
} |
|
], |
|
"x": 160, |
|
"y": 300, |
|
"wires": [ |
|
[ |
|
"9c83d20d034a8217" |
|
], |
|
[ |
|
"4a4efb4deeea3a24" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "6f16c88dad72878f", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "validate credentials", |
|
"func": " let UserId = (msg.payload.UserId || '').toLowerCase()\n let Password = msg.payload.Password || ''\n\n let UserRegistry = global.get('UserRegistry') || Object.create(null)\n if ((UserId in UserRegistry) && (UserRegistry[UserId] != null)) {\n let UserSpecs = UserRegistry[UserId]\n if (UserSpecs.Password === Password) { // internal optimization\n return withAuthorizationOf(UserId,UserSpecs.Roles || [])\n }\n\n let PBKDF2Iterations = global.get('PBKDF2Iterations') || 100000\n crypto.pbkdf2(\n Password, Buffer.from(UserSpecs.Salt,'hex'), PBKDF2Iterations, 64, 'sha512',\n function (Error, computedHash) {\n if ((Error == null) && (computedHash.toString('hex') === UserSpecs.Hash)) {\n UserSpecs.Password = Password // speeds up future auth. requests\n return withAuthorizationOf(UserId,UserSpecs.Roles || [])\n } else {\n return withoutAuthorization()\n }\n }\n )\n } else {\n return withoutAuthorization()\n }\n\n function withAuthorizationOf (UserName, UserRoles) {\n if ((msg.requiredRole == null) || (UserRoles.indexOf(msg.requiredRole) >= 0)) {\n msg.authenticatedUser = UserId\n msg.authorizedRoles = UserRoles\n\n node.send([msg,null])\n node.done()\n } else {\n return withoutAuthorization()\n }\n }\n\n function withoutAuthorization () {\n msg.cookies = msg.cookies || {}\n msg.cookies.authorization = { value:null }\n\n msg.payload = 'Unauthorized'\n msg.statusCode = 401\n\n node.send([null,msg])\n node.done()\n }\n", |
|
"outputs": 2, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [ |
|
{ |
|
"var": "crypto", |
|
"module": "crypto" |
|
} |
|
], |
|
"x": 150, |
|
"y": 420, |
|
"wires": [ |
|
[ |
|
"8586b1e3f59f5e67" |
|
], |
|
[ |
|
"0a60fc2822814b75" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "e5bd2fb29dfdf1f1", |
|
"type": "inject", |
|
"z": "d8fe099880fd703d", |
|
"name": "at Startup", |
|
"props": [ |
|
{ |
|
"p": "payload" |
|
}, |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": true, |
|
"onceDelay": 0.1, |
|
"topic": "", |
|
"payloadType": "date", |
|
"x": 110, |
|
"y": 160, |
|
"wires": [ |
|
[ |
|
"0b5dde3db674cb58" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "0b5dde3db674cb58", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "generate Token Key", |
|
"func": " let TokenKey = global.get('TokenKey')\n if (TokenKey == null) { // do not change TokenKey upon Node-RED deployment\n global.set('TokenKey',crypto.randomBytes(16).toString('hex'))\n }\n return msg\n", |
|
"outputs": 1, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [ |
|
{ |
|
"var": "crypto", |
|
"module": "crypto" |
|
} |
|
], |
|
"x": 320, |
|
"y": 160, |
|
"wires": [ |
|
[ |
|
"7c029d497f0dbc7d" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "7c029d497f0dbc7d", |
|
"type": "debug", |
|
"z": "d8fe099880fd703d", |
|
"name": "Status", |
|
"active": true, |
|
"tosidebar": false, |
|
"console": false, |
|
"tostatus": true, |
|
"complete": "true", |
|
"targetType": "full", |
|
"statusVal": "'token key generated'", |
|
"statusType": "jsonata", |
|
"x": 530, |
|
"y": 160, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "8586b1e3f59f5e67", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "create token", |
|
"func": " let Expiration = Date.now() + global.get('TokenLifetime')\n let TokenContent = msg.authenticatedUser + ':' + Expiration\n\n let TokenKey = global.get('TokenKey')\n const HMAC = crypto.createHmac('sha256',TokenKey)\n HMAC.update(TokenContent)\n let Digest = HMAC.digest('hex')\n\n msg.cookies = msg.cookies || {}\n msg.cookies.authorization = { value:TokenContent + ':' + Digest }\n return msg\n", |
|
"outputs": 1, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [ |
|
{ |
|
"var": "crypto", |
|
"module": "crypto" |
|
} |
|
], |
|
"x": 350, |
|
"y": 360, |
|
"wires": [ |
|
[ |
|
"b67fe1f3071cca07" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "9c83d20d034a8217", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "refresh token", |
|
"func": " let Expiration = Date.now() + global.get('TokenLifetime')\n let TokenContent = msg.authenticatedUser + ':' + Expiration\n\n let TokenKey = global.get('TokenKey')\n const HMAC = crypto.createHmac('sha256',TokenKey)\n HMAC.update(TokenContent)\n let Digest = HMAC.digest('hex')\n\n msg.cookies = msg.cookies || {}\n msg.cookies.authorization = { value:TokenContent + ':' + Digest }\n return msg\n", |
|
"outputs": 1, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [ |
|
{ |
|
"var": "crypto", |
|
"module": "crypto" |
|
} |
|
], |
|
"x": 330, |
|
"y": 240, |
|
"wires": [ |
|
[ |
|
"8f16cbb0cc28ee30" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "80106edae73330cd", |
|
"type": "inject", |
|
"z": "d8fe099880fd703d", |
|
"name": "at Startup", |
|
"props": [ |
|
{ |
|
"p": "payload" |
|
}, |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": true, |
|
"onceDelay": 0.1, |
|
"topic": "", |
|
"payloadType": "date", |
|
"x": 110, |
|
"y": 100, |
|
"wires": [ |
|
[ |
|
"e1e821402ba2abe1" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "e1e821402ba2abe1", |
|
"type": "change", |
|
"z": "d8fe099880fd703d", |
|
"name": "set token lifetime", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "TokenLifetime", |
|
"pt": "global", |
|
"to": "2*60*1000", |
|
"tot": "jsonata" |
|
}, |
|
{ |
|
"t": "set", |
|
"p": "payload", |
|
"pt": "msg", |
|
"to": "TokenLifetime", |
|
"tot": "global" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 310, |
|
"y": 100, |
|
"wires": [ |
|
[ |
|
"11640b19fd562bfb" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "11640b19fd562bfb", |
|
"type": "debug", |
|
"z": "d8fe099880fd703d", |
|
"name": "Status", |
|
"active": true, |
|
"tosidebar": false, |
|
"console": false, |
|
"tostatus": true, |
|
"complete": "true", |
|
"targetType": "full", |
|
"statusVal": "payload", |
|
"statusType": "msg", |
|
"x": 530, |
|
"y": 100, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "93bc823a9594e786", |
|
"type": "reusable-in", |
|
"z": "d8fe099880fd703d", |
|
"name": "cookie auth", |
|
"info": "describe your reusable flow here", |
|
"scope": "global", |
|
"x": 90, |
|
"y": 240, |
|
"wires": [ |
|
[ |
|
"07b8b379eee00444" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "a2c8904560f5d8e1", |
|
"type": "reusable-in", |
|
"z": "d8fe099880fd703d", |
|
"name": "cookie login", |
|
"info": "describe your reusable flow here", |
|
"scope": "global", |
|
"x": 90, |
|
"y": 360, |
|
"wires": [ |
|
[ |
|
"6f16c88dad72878f" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "8f16cbb0cc28ee30", |
|
"type": "reusable-out", |
|
"z": "d8fe099880fd703d", |
|
"name": "authorized", |
|
"position": 1, |
|
"x": 510, |
|
"y": 240, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "4a4efb4deeea3a24", |
|
"type": "reusable-out", |
|
"z": "d8fe099880fd703d", |
|
"name": "unauthorized", |
|
"position": "2", |
|
"x": 510, |
|
"y": 280, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "b67fe1f3071cca07", |
|
"type": "reusable-out", |
|
"z": "d8fe099880fd703d", |
|
"name": "success", |
|
"position": 1, |
|
"x": 520, |
|
"y": 360, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "0a60fc2822814b75", |
|
"type": "reusable-out", |
|
"z": "d8fe099880fd703d", |
|
"name": "failure", |
|
"position": "2", |
|
"x": 530, |
|
"y": 400, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "e7e4d431ae21b31c", |
|
"type": "comment", |
|
"z": "d8fe099880fd703d", |
|
"name": "try yourself", |
|
"info": "", |
|
"x": 80, |
|
"y": 500, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "bcce3b9c21368743", |
|
"type": "http in", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"url": "cookie-auth", |
|
"method": "get", |
|
"upload": false, |
|
"swaggerDoc": "", |
|
"x": 100, |
|
"y": 560, |
|
"wires": [ |
|
[ |
|
"a1d6fa210264c774" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "79102e3ebd685bba", |
|
"type": "http response", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"statusCode": "", |
|
"headers": {}, |
|
"x": 530, |
|
"y": 620, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "b146b96c1e6e4802", |
|
"type": "change", |
|
"z": "d8fe099880fd703d", |
|
"name": "inform about success", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "payload", |
|
"pt": "msg", |
|
"to": "successfully authorized", |
|
"tot": "str" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 340, |
|
"y": 620, |
|
"wires": [ |
|
[ |
|
"79102e3ebd685bba" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "bca7975301895f4d", |
|
"type": "http in", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"url": "cookie-auth", |
|
"method": "post", |
|
"upload": false, |
|
"swaggerDoc": "", |
|
"x": 110, |
|
"y": 680, |
|
"wires": [ |
|
[ |
|
"6ad54df9077948bd" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "5aa149195cb2bafd", |
|
"type": "change", |
|
"z": "d8fe099880fd703d", |
|
"name": "inform about success", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "payload", |
|
"pt": "msg", |
|
"to": "successfully authorized", |
|
"tot": "str" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 340, |
|
"y": 740, |
|
"wires": [ |
|
[ |
|
"9603dfde62c5f08b" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "9603dfde62c5f08b", |
|
"type": "http response", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"statusCode": "", |
|
"headers": {}, |
|
"x": 530, |
|
"y": 740, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "a1d6fa210264c774", |
|
"type": "reusable", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"target": "cookie auth", |
|
"outputs": 2, |
|
"x": 290, |
|
"y": 560, |
|
"wires": [ |
|
[ |
|
"b146b96c1e6e4802" |
|
], |
|
[ |
|
"79102e3ebd685bba" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "6ad54df9077948bd", |
|
"type": "reusable", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"target": "cookie login", |
|
"outputs": 2, |
|
"x": 310, |
|
"y": 680, |
|
"wires": [ |
|
[ |
|
"5aa149195cb2bafd" |
|
], |
|
[ |
|
"9603dfde62c5f08b" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "824216bdbb591cea", |
|
"type": "comment", |
|
"z": "d8fe099880fd703d", |
|
"name": "if used without \"node-red-within-express\"", |
|
"info": "", |
|
"x": 180, |
|
"y": 820, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "a58452a677c7be13", |
|
"type": "inject", |
|
"z": "d8fe099880fd703d", |
|
"name": "at Startup", |
|
"props": [ |
|
{ |
|
"p": "payload" |
|
}, |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": false, |
|
"onceDelay": 0.1, |
|
"topic": "", |
|
"payloadType": "date", |
|
"x": 100, |
|
"y": 880, |
|
"wires": [ |
|
[ |
|
"620a731991335960" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "dc02f08e694bc9ae", |
|
"type": "file in", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"filename": "./registeredUsers.json", |
|
"format": "utf8", |
|
"chunk": false, |
|
"sendError": false, |
|
"encoding": "utf8", |
|
"allProps": false, |
|
"x": 160, |
|
"y": 1000, |
|
"wires": [ |
|
[ |
|
"14f00e0115539df9" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "4690b38a2f0f4c4c", |
|
"type": "catch", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"scope": [ |
|
"dc02f08e694bc9ae", |
|
"14f00e0115539df9" |
|
], |
|
"uncaught": false, |
|
"x": 110, |
|
"y": 1060, |
|
"wires": [ |
|
[ |
|
"751a4b5f6d827646" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "f47143ada81d0161", |
|
"type": "debug", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"active": true, |
|
"tosidebar": true, |
|
"console": false, |
|
"tostatus": true, |
|
"complete": "true", |
|
"targetType": "full", |
|
"statusVal": "'could not load user registry'", |
|
"statusType": "jsonata", |
|
"x": 510, |
|
"y": 1060, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "4a700dfcceee780a", |
|
"type": "debug", |
|
"z": "d8fe099880fd703d", |
|
"name": "Status", |
|
"active": true, |
|
"tosidebar": false, |
|
"console": false, |
|
"tostatus": true, |
|
"complete": "true", |
|
"targetType": "full", |
|
"statusVal": "'user registry available'", |
|
"statusType": "jsonata", |
|
"x": 530, |
|
"y": 880, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "751a4b5f6d827646", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "create in global context", |
|
"func": " let UserRegistry = Object.create(null)\n UserRegistry['node-red'] = {\n Roles: ['node-red'],\n Salt: '4486e8d35b8275020b1301226cc77963',\n Hash: 'ab2b740ea9148aa4f320af3f3ba60ee2e33bb8039c57eea2b29579ff3f3b16bec2401f19e3c6ed8ad36de432b80b6f973a12c41af5d50738e4bb902d0117df53'\n }\n global.set('UserRegistry',UserRegistry)\n\n return msg\n", |
|
"outputs": 1, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [], |
|
"x": 310, |
|
"y": 1060, |
|
"wires": [ |
|
[ |
|
"f47143ada81d0161", |
|
"8318c57d9522aa33" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "8494471c9c395698", |
|
"type": "file", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"filename": "./registeredUsers.json", |
|
"appendNewline": false, |
|
"createDir": false, |
|
"overwriteFile": "true", |
|
"encoding": "utf8", |
|
"x": 420, |
|
"y": 1180, |
|
"wires": [ |
|
[ |
|
"5432a3f68d4f0c24" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "6c62fbae4d2acb44", |
|
"type": "catch", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"scope": [ |
|
"8494471c9c395698" |
|
], |
|
"uncaught": false, |
|
"x": 110, |
|
"y": 1300, |
|
"wires": [ |
|
[ |
|
"165a1ce2e9945426", |
|
"dd9bbfd781f6ea50" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "165a1ce2e9945426", |
|
"type": "debug", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"active": true, |
|
"tosidebar": true, |
|
"console": false, |
|
"tostatus": true, |
|
"complete": "true", |
|
"targetType": "full", |
|
"statusVal": "'could not write user registry'", |
|
"statusType": "jsonata", |
|
"x": 250, |
|
"y": 1340, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "5432a3f68d4f0c24", |
|
"type": "change", |
|
"z": "d8fe099880fd703d", |
|
"name": "restore payload", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "payload", |
|
"pt": "msg", |
|
"to": "_payload", |
|
"tot": "msg" |
|
}, |
|
{ |
|
"t": "delete", |
|
"p": "_payload", |
|
"pt": "msg" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 180, |
|
"y": 1240, |
|
"wires": [ |
|
[ |
|
"6b8835c3f66d4f0b" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "dd9bbfd781f6ea50", |
|
"type": "change", |
|
"z": "d8fe099880fd703d", |
|
"name": "report in payload", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "payload", |
|
"pt": "msg", |
|
"to": "'Internal Server Error'", |
|
"tot": "jsonata" |
|
}, |
|
{ |
|
"t": "set", |
|
"p": "statusCode", |
|
"pt": "msg", |
|
"to": "500", |
|
"tot": "str" |
|
}, |
|
{ |
|
"t": "delete", |
|
"p": "_payload", |
|
"pt": "msg" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 290, |
|
"y": 1300, |
|
"wires": [ |
|
[ |
|
"6b8835c3f66d4f0b" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "14f00e0115539df9", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "write to global context", |
|
"func": " let UserSet = JSON.parse(msg.payload) // may fail!\n\n let UserRegistry = Object.create(null)\n for (let UserId in UserSet) {\n if (UserSet.hasOwnProperty(UserId)) {\n if ((UserId.indexOf('/') >= 0) || (UserId.indexOf(':') >= 0)) {\n throw 'Invalid character in UserId found'\n }\n \n UserRegistry[UserId.toLowerCase()] = UserSet[UserId]\n }\n }\n global.set('UserRegistry',UserRegistry)\n\n msg.payload = ''\n return msg\n", |
|
"outputs": 1, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [], |
|
"x": 400, |
|
"y": 1000, |
|
"wires": [ |
|
[ |
|
"8318c57d9522aa33" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "5cc4a021d6704942", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "→ catch", |
|
"func": "// do not pass any msg from here!", |
|
"outputs": 1, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [], |
|
"x": 360, |
|
"y": 940, |
|
"wires": [ |
|
[ |
|
"751a4b5f6d827646" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "94df8f09e02b2af2", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "→ catch", |
|
"func": "// do not pass any msg from here!", |
|
"outputs": 1, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [], |
|
"x": 300, |
|
"y": 1120, |
|
"wires": [ |
|
[ |
|
"dd9bbfd781f6ea50" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "ddd48700d0596225", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "read from global context", |
|
"func": " let UserRegistry = global.get('UserRegistry')\n let UserSet = {}\n for (let UserId in UserRegistry) {\n if (UserRegistry[UserId] == null) {\n UserSet[UserId] = null\n } else {\n let UserEntry = Object.assign({},UserRegistry[UserId])\n delete UserEntry.Password // never write passwords in plain text!\n UserSet[UserId] = UserEntry\n }\n }\n\n msg.payload = JSON.stringify(UserSet)\n return msg\n", |
|
"outputs": 1, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [], |
|
"x": 170, |
|
"y": 1180, |
|
"wires": [ |
|
[ |
|
"8494471c9c395698" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "8318c57d9522aa33", |
|
"type": "reusable-out", |
|
"z": "d8fe099880fd703d", |
|
"name": "return", |
|
"position": 1, |
|
"x": 530, |
|
"y": 940, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "6b8835c3f66d4f0b", |
|
"type": "reusable-out", |
|
"z": "d8fe099880fd703d", |
|
"name": "return", |
|
"position": 1, |
|
"x": 530, |
|
"y": 1240, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "483c14063ef038dd", |
|
"type": "reusable-in", |
|
"z": "d8fe099880fd703d", |
|
"name": "load or create UserRegistry", |
|
"info": "describe your reusable flow here", |
|
"scope": "global", |
|
"x": 140, |
|
"y": 940, |
|
"wires": [ |
|
[ |
|
"5cc4a021d6704942", |
|
"dc02f08e694bc9ae" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "5e75962e8f9a13df", |
|
"type": "reusable-in", |
|
"z": "d8fe099880fd703d", |
|
"name": "write UserRegistry", |
|
"info": "describe your reusable flow here", |
|
"scope": "global", |
|
"x": 110, |
|
"y": 1120, |
|
"wires": [ |
|
[ |
|
"94df8f09e02b2af2", |
|
"ddd48700d0596225" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "620a731991335960", |
|
"type": "reusable", |
|
"z": "d8fe099880fd703d", |
|
"name": "", |
|
"target": "load or create userregistry", |
|
"outputs": 1, |
|
"x": 320, |
|
"y": 880, |
|
"wires": [ |
|
[ |
|
"4a700dfcceee780a" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "ad7e203958af3b87", |
|
"type": "inject", |
|
"z": "d8fe099880fd703d", |
|
"name": "show UserRegistry", |
|
"props": [ |
|
{ |
|
"p": "payload" |
|
}, |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": false, |
|
"onceDelay": 0.1, |
|
"topic": "", |
|
"payloadType": "date", |
|
"x": 130, |
|
"y": 1480, |
|
"wires": [ |
|
[ |
|
"4b46df7714a99db8" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "3005efbfdca160fb", |
|
"type": "comment", |
|
"z": "d8fe099880fd703d", |
|
"name": "for testing purposes", |
|
"info": "", |
|
"x": 110, |
|
"y": 1420, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "4b46df7714a99db8", |
|
"type": "function", |
|
"z": "d8fe099880fd703d", |
|
"name": "create output", |
|
"func": "let UserRegistry = global.get('UserRegistry') || Object.create(null)\n let UserList = []\n for (let UserId in UserRegistry) {\n UserList.push(\n UserRegistry[UserId] == null ? '[' + UserId + ']' : UserId\n )\n }\nmsg.payload = (\n UserList.length === 0\n ? '(no user registered)'\n : 'registered users: \"' + UserList.join('\",\"') + '\"'\n)\nreturn msg", |
|
"outputs": 1, |
|
"noerr": 0, |
|
"initialize": "", |
|
"finalize": "", |
|
"libs": [], |
|
"x": 340, |
|
"y": 1480, |
|
"wires": [ |
|
[ |
|
"164c9be0f8434f61" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "164c9be0f8434f61", |
|
"type": "debug", |
|
"z": "d8fe099880fd703d", |
|
"name": "show", |
|
"active": true, |
|
"tosidebar": true, |
|
"console": false, |
|
"tostatus": false, |
|
"complete": "payload", |
|
"targetType": "msg", |
|
"statusVal": "", |
|
"statusType": "auto", |
|
"x": 510, |
|
"y": 1480, |
|
"wires": [] |
|
} |
|
] |