Example of a simplified dashboard security system for local networks. (Privacy)

I started with Node-red a short time ago and also felt the need for a credential system for my dashboard a lot. As there is nothing native, I created a simple system, a "sticker".
Many beginners, even those who know very little about programming, from time to time have the same need.

That's why I decided to share this simple flow for locking and unlocking menus with password and user.

Just implement the code, insert the user and password you want in the function node responsible for the comparison and insert the other menus that will be hidden or shown as exemplified in the flow.

An interesting point is that the flow redirects the user to login, if he tries to access the tabs through the link with permission denied, for example.

I emphasize that it is a simple system, to be implemented in local applications, without exposure to the internet. In these cases there is no security.

the idea is just to have a kind of privacy at a local level, not leaving the guides so exposed.

I hope it will be useful for some beginner as well as me.

[{"id":"64c3a11c.be78a","type":"ui_form","z":"626d4279.e54a0c","name":"Form for entering data","label":"","group":"4e15518d.02b1a","order":1,"width":6,"height":3,"options":[{"label":"User","value":"User","type":"text","required":true,"rows":null},{"label":"Password","value":"Password","type":"password","required":true,"rows":null}],"formValue":{"User":"","Password":""},"payload":"","submit":"OK","cancel":"Cancel","topic":"mi","x":440,"y":360,"wires":[["4481aeff.d1662","3cca91f3.b0ed6e"]]},{"id":"5702ed7b.667e54","type":"ui_ui_control","z":"626d4279.e54a0c","name":"","events":"change","x":1340,"y":360,"wires":[["6ff5ae08.7992b"]]},{"id":"138bb241.8243ee","type":"ui_ui_control","z":"626d4279.e54a0c","name":"","events":"all","x":1320,"y":840,"wires":[[]]},{"id":"2880f2d8.ea9e4e","type":"join","z":"626d4279.e54a0c","name":"returns the current authorization if the system is \"moved\"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"1","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":950,"y":460,"wires":[["aa498102.d0df3","ea0e0132.070e6","2a42864d.3190da"]]},{"id":"4481aeff.d1662","type":"function","z":"626d4279.e54a0c","name":"Verify password/user","func":"if ((msg.payload.User == \"admin\")&& (msg.payload.Password == \"123\"))\n{msg.payload = \"granted\"\nmsg.topic = \"authorization\"\n    return msg;\n    \n}\nelse \n{\n    msg.payload = \"denied\"\n    msg.topic = \"authorization\"\nreturn msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":740,"y":300,"wires":[["2880f2d8.ea9e4e","606b8d73.f27144"]]},{"id":"6ff5ae08.7992b","type":"function","z":"626d4279.e54a0c","name":"redirects to login if authorization = denied ","func":"\nmsg.topic = \"tab\"\nmsg.payload = msg.tab;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1440,"y":420,"wires":[["2880f2d8.ea9e4e"]]},{"id":"aa498102.d0df3","type":"function","z":"626d4279.e54a0c","name":"Flashing LEDs","func":"if (msg.payload.authorization == \"denied\")\n{\nmsg.payload = {\"tabs\": {\"hide\": \"Flashing LEDs\", \"disable\": [\"secret_tab\", \"unused_stuff\"]}}\nreturn msg;\n}\nif (msg.payload.authorization == \"granted\")\n{\nmsg.payload = {\"tabs\": {\"show\": \"Flashing LEDs\", \"enabled\": [\"secret_tab\", \"unused_stuff\"]}}\nreturn msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":920,"y":660,"wires":[["138bb241.8243ee"]]},{"id":"3cca91f3.b0ed6e","type":"function","z":"626d4279.e54a0c","name":"Verify failure notification","func":"if ((msg.payload.User == \"admin\")&& (msg.payload.Password == \"123\"))\n{\n    msg.payload = \" \"\n    return msg;\n    \n}\nelse \n{\n    msg.payload = \"Incorrect username or password.\"\n    msg.topic = \"authorization\"\nreturn msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":750,"y":340,"wires":[["bbc9ae5a.da221"]]},{"id":"bbc9ae5a.da221","type":"ui_text","z":"626d4279.e54a0c","group":"4e15518d.02b1a","order":2,"width":6,"height":1,"name":"Notification","label":"","format":"<font color = red >{{msg.payload}}</font>","layout":"col-center","x":710,"y":380,"wires":[]},{"id":"741ca1e6.cb9f2","type":"ui_button","z":"626d4279.e54a0c","name":"exit button","group":"f590a74e.55cc88","order":1,"width":6,"height":1,"passthru":false,"label":" Go out","tooltip":"","color":"","bgcolor":"","icon":"","payload":"denied","payloadType":"str","topic":"authorization","x":1110,"y":300,"wires":[["2880f2d8.ea9e4e","555593ed.25976c"]]},{"id":"95da46b4.8883a8","type":"ui_ui_control","z":"626d4279.e54a0c","name":"","events":"all","x":1320,"y":900,"wires":[[]]},{"id":"4c18c95c.70a4c8","type":"function","z":"626d4279.e54a0c","name":"redirects to start tab if authorization = granted","func":"if (msg.payload == \"granted\")\n{\n    msg.payload = {\"tab\":\"start\"}\n    return msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":750,"y":880,"wires":[["95da46b4.8883a8","84cb9151.ecbb2","27a32586.def47a"]]},{"id":"606b8d73.f27144","type":"delay","z":"626d4279.e54a0c","name":"","pauseType":"delay","timeout":"0.5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":650,"y":680,"wires":[["4c18c95c.70a4c8"]]},{"id":"84cb9151.ecbb2","type":"function","z":"626d4279.e54a0c","name":"clear user ","func":"\nmsg.payload.User = ''\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":320,"y":840,"wires":[["64c3a11c.be78a"]]},{"id":"27a32586.def47a","type":"function","z":"626d4279.e54a0c","name":"clear password","func":"msg.payload.Password = ''\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":340,"y":920,"wires":[["64c3a11c.be78a"]]},{"id":"555593ed.25976c","type":"function","z":"626d4279.e54a0c","name":"show the login tab","func":"msg.payload = {\"tabs\": {\"show\": \"Login\", \"enabled\": [\"secret_tab\", \"unused_stuff\"]}}  \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1350,"y":300,"wires":[["5702ed7b.667e54"]]},{"id":"ea0e0132.070e6","type":"function","z":"626d4279.e54a0c","name":"hide login if authorization is = granted","func":"if (msg.payload.authorization == \"denied\")\n{\n    msg.payload = {\"tab\":\"Login\"}\n    return msg;\n}\nif (msg.payload.authorization == \"granted\")\n{\nmsg.payload = {\"tabs\": {\"hide\": \"Login\", \"disable\": [\"secret_tab\", \"unused_stuff\"]}}  \nreturn msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1430,"y":500,"wires":[["138bb241.8243ee"]]},{"id":"d6d2765.6067288","type":"inject","z":"626d4279.e54a0c","name":"Initial injection. Locks on startup.","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":440,"y":300,"wires":[["4481aeff.d1662"]]},{"id":"82b32ccc.1e3f2","type":"comment","z":"626d4279.e54a0c","name":"these function nodes are where the other tabs are inserted. If permission is denied they are inaccessible and vice versa.","info":"\nthese function nodes are where the other tabs are inserted. If permission is denied they are inaccessible and vice versa.","x":940,"y":560,"wires":[]},{"id":"2a42864d.3190da","type":"function","z":"626d4279.e54a0c","name":"start tab","func":"if (msg.payload.authorization == \"denied\")\n{\nmsg.payload = {\"tabs\": {\"hide\": \"start\", \"disable\": [\"secret_tab\", \"unused_stuff\"]}}\nreturn msg;\n}\nif (msg.payload.authorization == \"granted\")\n{\nmsg.payload = {\"tabs\": {\"show\": \"start\", \"enabled\": [\"secret_tab\", \"unused_stuff\"]}}\nreturn msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":900,"y":600,"wires":[["138bb241.8243ee"]]},{"id":"4e15518d.02b1a","type":"ui_group","name":"indicate your credentials","tab":"d9365e0f.e1bf6","order":1,"disp":true,"width":"6","collapse":false},{"id":"f590a74e.55cc88","type":"ui_group","name":"Default","tab":"5677487e.117ef8","order":1,"disp":true,"width":"6","collapse":false},{"id":"d9365e0f.e1bf6","type":"ui_tab","name":"Login","icon":"lock","order":18,"disabled":false,"hidden":false},{"id":"5677487e.117ef8","type":"ui_tab","name":"start","icon":"dashboard","disabled":false,"hidden":false}]

I'm not attacking you. I started out just like this trying to make a user login for the dashboard 3 years ago. I hope to save you some time and misery.
BUT - this is not secure!!!

Any one can see what the other user is doing. All I have todo is sit at url http(s)://youriphere:1880/ui/#!/0 or 1 or 2 or etc and wait for an authorized user to unhide the page for me. give it a try. open 2 pages with different socket id's one not logged in and the other logged in. You will not be happy with what you see.

Oh I've created one hell of a login system for users of the dashboard but I ended up having to use mdashboard to pull it off so I can keep everyone isolated by socketid's and ipaddress checks and a increase login timer for bad actors and JWT to check that the ip did not change as users move to something like worldmap. bcrypt to make sure passwords are not stored in plain txt.

What you have done is fine for a local network that never sees the internet.

Why you never tend to see a howto on this is that people that create a user login dont want any one else to see how to break into it or like me embarrassed to show any one else what they created because the will get tore up from the floor up. I hit a bit of each of those categories..... That stops today.

I'm going to tackle you login system and hand you back something that others can share.

The idea is precisely to use the system on a local network, maybe I didn't make that clear. In my case, the network where I run an application is accessed by several people.
There's always an eavesdropper who'll take a look, of course, with no ill intentions. However, in these glances, some undue change can be made.

Anyway, as I said, it's a simple thing, for simple applications and on local networks. Nothing more.

But it was good for you to highlight that, I'll try to make it clearer. Thanks.

1 Like

This looks like fun to play with on my local network... keep my pesky cats from blinking my LEDs or something. Now how to prevent them from turning on the vacuum (true story, just a few days ago). Granted the shock of that thing starting up might have done the job :stuck_out_tongue:

I do notice that incorrectly logging in gives error msg, but correctly logging in has no affect at all. It took me awhile to realize that the lack of error was indicative of correct login.

Looking at the code, isn't it supposed to blank out or disable the login display?

I am a bit stuck at this stage... not sure how, what and where to add whatever I want hidden.

2 Likes

oh sorry there was a little mistake. I've already corrected. Now the login hides.

Well, I don't know how you organized your dashboard. If you have placed all your LEDs on one tab (for example the "LEDS" tab - of course haha), go to the tab 1 function node and replace "sensor 1" with the name of your tab. If you have several tabs, repeat the process, in each function node (tab 1, tab 2, keep replacing the example name with the name of your current tab.

I've got the login page working natively in dashboard. Working on the create new account and i lost may password stuff now. This is going to take all night but @Joaovitor motivated me to share my work.

I think I'll do two versions 1 with JWT and one without for just the dashboard.

eye candy to get ya all to check back on this thread tomorrow.

Just have a few more multi user dash things to remove

Screenshot from 2021-07-30 21-31-58

@Gunner Cat story made my day. Out of curiosity what time did this happen at? im thinking asleep at 3am would make it even more scary situation.

1 Like

Hah, tab name is Flashing LEDs in fact :smiley:

Thanks, with that handholding I figured that out... however, it actually locks me out of every tab??... had me hyperventilating for a few milliseconds as I hoped I could remember the default credentials :laughing:

It would have made my day if they would have at least tried to follow up with some actual use of the thing to do some cleanup... it is mostly all their own hair anyhow... I can't afford to shed anymore of my own :man_bald:

1 Like

See if it works! I entered the menu you mentioned

[{"id":"64c3a11c.be78a","type":"ui_form","z":"626d4279.e54a0c","name":"Form for entering data","label":"","group":"4e15518d.02b1a","order":1,"width":6,"height":3,"options":[{"label":"User","value":"User","type":"text","required":true,"rows":null},{"label":"Password","value":"Password","type":"password","required":true,"rows":null}],"formValue":{"User":"","Password":""},"payload":"","submit":"OK","cancel":"Cancel","topic":"mi","x":440,"y":360,"wires":[["4481aeff.d1662","3cca91f3.b0ed6e"]]},{"id":"5702ed7b.667e54","type":"ui_ui_control","z":"626d4279.e54a0c","name":"","events":"change","x":1340,"y":360,"wires":[["6ff5ae08.7992b"]]},{"id":"138bb241.8243ee","type":"ui_ui_control","z":"626d4279.e54a0c","name":"","events":"all","x":1320,"y":840,"wires":[[]]},{"id":"2880f2d8.ea9e4e","type":"join","z":"626d4279.e54a0c","name":"returns the current authorization if the system is \"moved\"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"1","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":950,"y":460,"wires":[["aa498102.d0df3","ea0e0132.070e6","2a42864d.3190da"]]},{"id":"4481aeff.d1662","type":"function","z":"626d4279.e54a0c","name":"Verify password/user","func":"if ((msg.payload.User == \"admin\")&& (msg.payload.Password == \"123\"))\n{msg.payload = \"granted\"\nmsg.topic = \"authorization\"\n    return msg;\n    \n}\nelse \n{\n    msg.payload = \"denied\"\n    msg.topic = \"authorization\"\nreturn msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":740,"y":300,"wires":[["2880f2d8.ea9e4e","606b8d73.f27144"]]},{"id":"6ff5ae08.7992b","type":"function","z":"626d4279.e54a0c","name":"redirects to login if authorization = denied ","func":"\nmsg.topic = \"tab\"\nmsg.payload = msg.tab;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1440,"y":420,"wires":[["2880f2d8.ea9e4e"]]},{"id":"aa498102.d0df3","type":"function","z":"626d4279.e54a0c","name":"Flashing LEDs","func":"if (msg.payload.authorization == \"denied\")\n{\nmsg.payload = {\"tabs\": {\"hide\": \"Flashing LEDs\", \"disable\": [\"secret_tab\", \"unused_stuff\"]}}\nreturn msg;\n}\nif (msg.payload.authorization == \"granted\")\n{\nmsg.payload = {\"tabs\": {\"show\": \"Flashing LEDs\", \"enabled\": [\"secret_tab\", \"unused_stuff\"]}}\nreturn msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":920,"y":660,"wires":[["138bb241.8243ee"]]},{"id":"3cca91f3.b0ed6e","type":"function","z":"626d4279.e54a0c","name":"Verify failure notification","func":"if ((msg.payload.User == \"admin\")&& (msg.payload.Password == \"123\"))\n{\n    msg.payload = \" \"\n    return msg;\n    \n}\nelse \n{\n    msg.payload = \"Incorrect username or password.\"\n    msg.topic = \"authorization\"\nreturn msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":750,"y":340,"wires":[["bbc9ae5a.da221"]]},{"id":"bbc9ae5a.da221","type":"ui_text","z":"626d4279.e54a0c","group":"4e15518d.02b1a","order":2,"width":6,"height":1,"name":"Notification","label":"","format":"<font color = red >{{msg.payload}}</font>","layout":"col-center","x":710,"y":380,"wires":[]},{"id":"741ca1e6.cb9f2","type":"ui_button","z":"626d4279.e54a0c","name":"exit button","group":"f590a74e.55cc88","order":1,"width":6,"height":1,"passthru":false,"label":" Go out","tooltip":"","color":"","bgcolor":"","icon":"","payload":"denied","payloadType":"str","topic":"authorization","x":1110,"y":300,"wires":[["2880f2d8.ea9e4e","555593ed.25976c"]]},{"id":"95da46b4.8883a8","type":"ui_ui_control","z":"626d4279.e54a0c","name":"","events":"all","x":1320,"y":900,"wires":[[]]},{"id":"4c18c95c.70a4c8","type":"function","z":"626d4279.e54a0c","name":"redirects to start tab if authorization = granted","func":"if (msg.payload == \"granted\")\n{\n    msg.payload = {\"tab\":\"start\"}\n    return msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":750,"y":880,"wires":[["95da46b4.8883a8","84cb9151.ecbb2","27a32586.def47a"]]},{"id":"606b8d73.f27144","type":"delay","z":"626d4279.e54a0c","name":"","pauseType":"delay","timeout":"0.5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":650,"y":680,"wires":[["4c18c95c.70a4c8"]]},{"id":"84cb9151.ecbb2","type":"function","z":"626d4279.e54a0c","name":"clear user ","func":"\nmsg.payload.User = ''\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":320,"y":840,"wires":[["64c3a11c.be78a"]]},{"id":"27a32586.def47a","type":"function","z":"626d4279.e54a0c","name":"clear password","func":"msg.payload.Password = ''\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":340,"y":920,"wires":[["64c3a11c.be78a"]]},{"id":"555593ed.25976c","type":"function","z":"626d4279.e54a0c","name":"show the login tab","func":"msg.payload = {\"tabs\": {\"show\": \"Login\", \"enabled\": [\"secret_tab\", \"unused_stuff\"]}}  \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1350,"y":300,"wires":[["5702ed7b.667e54"]]},{"id":"ea0e0132.070e6","type":"function","z":"626d4279.e54a0c","name":"hide login if authorization is = granted","func":"if (msg.payload.authorization == \"denied\")\n{\n    msg.payload = {\"tab\":\"Login\"}\n    return msg;\n}\nif (msg.payload.authorization == \"granted\")\n{\nmsg.payload = {\"tabs\": {\"hide\": \"Login\", \"disable\": [\"secret_tab\", \"unused_stuff\"]}}  \nreturn msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1430,"y":500,"wires":[["138bb241.8243ee"]]},{"id":"d6d2765.6067288","type":"inject","z":"626d4279.e54a0c","name":"Initial injection. Locks on startup.","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":440,"y":300,"wires":[["4481aeff.d1662"]]},{"id":"82b32ccc.1e3f2","type":"comment","z":"626d4279.e54a0c","name":"these function nodes are where the other tabs are inserted. If permission is denied they are inaccessible and vice versa.","info":"\nthese function nodes are where the other tabs are inserted. If permission is denied they are inaccessible and vice versa.","x":940,"y":560,"wires":[]},{"id":"2a42864d.3190da","type":"function","z":"626d4279.e54a0c","name":"start tab","func":"if (msg.payload.authorization == \"denied\")\n{\nmsg.payload = {\"tabs\": {\"hide\": \"start\", \"disable\": [\"secret_tab\", \"unused_stuff\"]}}\nreturn msg;\n}\nif (msg.payload.authorization == \"granted\")\n{\nmsg.payload = {\"tabs\": {\"show\": \"start\", \"enabled\": [\"secret_tab\", \"unused_stuff\"]}}\nreturn msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":900,"y":600,"wires":[["138bb241.8243ee"]]},{"id":"4e15518d.02b1a","type":"ui_group","name":"indicate your credentials","tab":"d9365e0f.e1bf6","order":1,"disp":true,"width":"6","collapse":false},{"id":"f590a74e.55cc88","type":"ui_group","name":"Default","tab":"5677487e.117ef8","order":1,"disp":true,"width":"6","collapse":false},{"id":"d9365e0f.e1bf6","type":"ui_tab","name":"Login","icon":"lock","order":18,"disabled":false,"hidden":false},{"id":"5677487e.117ef8","type":"ui_tab","name":"start","icon":"dashboard","disabled":false,"hidden":false}]

That works... what was the difference?

Just enter the name of your "Flashing LEDs" menu in the function node of the same name. See the photo. If you have more menus, you can put more function nodes.

Working hard on the user login system for the dashboard.

Completed:
User login and tracking by socketid tied to socketip in context
Remove users from context when they disconnect
Check and redirect if user accesses a secure page or url # to login page
New account request checks completed so users can't request a name or email already in use. checks that passwords match.
Added global settings for max logins and max failed attempts so they can be set by admin
LOG file now reports failed attempts and page connections for users logged in or not

Working on
context storage for New account requests so admin and approve or deny them.

ToDo HUGE !! but its coming along.

Its been a struggle bringing this all into a node-red flow that has only dashboard and bcrypt requirements. I was using a database, encryption,nginx and JWT to get this done. I'm over the hump but its taken a good amount of function nodes to pull off :slight_smile:



2 Likes

Ive tried to make it easy for users to add security to their tabs

Just put this infront of the tab you want to secure

And it will send a user back to login page. after user logs in it remembers where they came from and sends them back to the tab(page) the were trying to reach; by the left hand menu or if the entered the url in the browser

1 Like

Look nice... but I have put this picture in front of every tab and nothing seems any more secure :laughing:

1 Like

@Gunner
I'll get it done... may take a week. I'm going to stop bombing this thread. I'm going to open a new one when its ready for release. I'll msg you and a few other with the link when I get there.

3 Likes