Hide all flows other than the ID in the current URL

Hello everyone. I have a requirement of opening only the flow whose ID is specified in the URL while opening Node-RED. After spending some time, I was able to achieve the desired results. I was wondering if I could make a pull request and make this an official feature. Since I am done developing it, I will share my code snippet as well. Following is the new loadFlows function along with all the changes. I have also added comments (line 20 & line 24). Let me know what do you think. Thank you.

function loadFlows(done) {
        loader.reportProgress(RED._("event.loadFlows"), 80)
        $.ajax({
            headers: {
                "Accept": "application/json",
            },
            cache: false,
            url: 'flows',
            success: function (nodes) {
                if (nodes) {
                    var currentHash = window.location.hash;
                    RED.nodes.version(nodes.rev);
                    loader.reportProgress(RED._("event.importFlows"), 90)
                    try {
                        RED.nodes.import(nodes.flows);
                        RED.nodes.dirty(false);
                        RED.view.redraw(true);
                        if (/^#(flow|node|group)\/.+$/.test(currentHash)) {
                            const hashParts = currentHash.split('/');
                            // check if "hideOtherFlows" parameter is passed
                            const hideOtherFlows = hashParts.length > 2 && (hashParts[2] === 'hideOtherFlows' || hashParts[3] === 'hideOtherFlows');
                            const showEditDialog = hashParts.length > 2 && hashParts[2] === 'edit';
                            if (hashParts[0] === '#flow') {
                                // if parameter is passed, hide all flows initially
                                // the flow having id specified in url will be made  visible later
                                if (hideOtherFlows) {
                                    const workspaces = RED.nodes.getWorkspaceOrder();
                                    for (let index = 0; index < workspaces.length; index++) {
                                        RED.workspaces.hide(workspaces[index])
                                    }
                                }
                                RED.workspaces.show(hashParts[1], true);
                                if (showEditDialog) {
                                    RED.workspaces.edit()
                                }
                            } else if (hashParts[0] === '#node') {
                                const nodeToShow = RED.nodes.node(hashParts[1])
                                if (nodeToShow) {
                                    setTimeout(() => {
                                        RED.view.reveal(nodeToShow.id)
                                        window.location.hash = currentHash
                                        RED.view.select(nodeToShow.id)
                                        if (showEditDialog) {
                                            RED.editor.edit(nodeToShow)
                                        }
                                    }, 50)
                                }
                            } else if (hashParts[0] === '#group') {
                                const nodeToShow = RED.nodes.group(hashParts[1])
                                if (nodeToShow) {
                                    RED.view.reveal(nodeToShow.id)
                                    window.location.hash = currentHash
                                    RED.view.select(nodeToShow.id)
                                    if (showEditDialog) {
                                        RED.editor.editGroup(nodeToShow)
                                    }
                                }
                            }
                        }
                        if (RED.workspaces.count() > 0) {
                            const hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs") || "{}");
                            const workspaces = RED.nodes.getWorkspaceOrder();
                            if (RED.workspaces.active() === 0) {
                                for (let index = 0; index < workspaces.length; index++) {
                                    const ws = workspaces[index];
                                    if (!hiddenTabs[ws]) {
                                        RED.workspaces.show(ws);
                                        break;
                                    }
                                }
                            }
                            if (RED.workspaces.active() === 0) {
                                RED.workspaces.show(workspaces[0]);
                            }
                        }
                    } catch (err) {
                        console.warn(err);
                        RED.notify(
                            RED._("event.importError", { message: err.message }),
                            {
                                fixed: true,
                                type: 'error'
                            }
                        );
                    }
                }
                done();
            }
        });
    }

Hi Ahmad,
Should be in the Feature Request category :wink:

I Moved it

And welcome to the forums @Ahmad44452

1 Like

Hi @Ahmad44452

Can I ask why? I mean it is fairly innocuous and I don't see any reason to say no but I am not certain what the value is?

1 Like

Sorry, seems to be my season for scepticism but I'm not convinced this is wise.

As far as I can see, it doesn't really securely prevent access to the other flows? Couldn't a custom node undo it for example? Only on my phone so can't really check.

If the purpose of this change is security, would need more thought?

I never thought it did Julian. And the reason I was asking. If it's for viewing purposes only (e.g. for training env or for quick management via hyperlinks to particular functionality) then I'm cool (i.e. it's innocuous) but if it's some kinda faux security thing, I'm not as it could give users a false sense of security.

1 Like

If the goal is just to open a particular flow - all you need is the URL of the flow (already implemented)

If the goal is to restrict access to a flow - it is not by changing loadFlow that it will effectively change things. With this approach only one flow will be loaded, which can pose a problem during a full deployment. If we take the root of the URL we will have access to all flows.

It's not as simple as a request

Thank you!

@Steve-Mcl we are trying to embed Node-RED in our web app where users can build some flow using Node-RED, which then will be used to perform certain execution. These will be trusted users and will be authenticated beforehand but we were looking to make some changes in order to give a better UI experience.

The user will basically be creating flow with the click of a button on our web-page (which will be done by hitting Node-RED admin API) then that newly created flow will be opened in an iframe where that user can create any kind of flow. To give a better UI, we were looking for quite a few changes, for example, disabling the add flow button within Node-RED, hiding all the irrelevant tabs (the current feature request) etc.

As giving access to the user only to the current flow will require a lot more thought and work, that is why we decided to go this route for now and make it UI friendly only. That is why I made this general feature request, I think everyone could benefit from. It can be used to hide all irrelevant tabs, ONLY on startup. I am open to suggestions, if there is a better way to do this or if there is a way we can give access to user only to the current flow.

While I am on it, I want to ask another question. Is it possible to have users who can make changes to the flows but not to the settings? Like to have separate super admins that have the complete access?

Do you mean settings.js? If so then make it only writable by root, so that it is necessary to use sudo to access it.

1 Like

Not just settings.js but also things like creating and deleting flow, managing palette etc. Like if I want one user-base to have only read access, one user-base to have the access to change the flows only (they can change the nodes as they want in a flow but cannot add or delete the flow itself) and one user-base to have full control over everything.

No, I believe that is currently not possible.

1 Like

Turn off the palette manager and control node installs outside node red.

Preventing crate or delete of flow files will need fine grained ACL controls that let need red update the file but not crafted or delete files in the folder.

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.