-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #977 from FlowFuse/947-client-constraints
Multi Tenancy: Ensure all conditions are met defined in _client constraints
- Loading branch information
Showing
4 changed files
with
309 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
[ | ||
{ | ||
"id": "160e82270b278a06", | ||
"type": "ui-slider", | ||
"z": "a725245cfaf96f83", | ||
"group": "32fcb041e36de17e", | ||
"name": "", | ||
"label": "Single Client", | ||
"tooltip": "", | ||
"order": 4, | ||
"width": 0, | ||
"height": 0, | ||
"passthru": false, | ||
"outs": "all", | ||
"topic": "topic", | ||
"topicType": "msg", | ||
"thumbLabel": true, | ||
"min": 0, | ||
"max": 10, | ||
"step": 1, | ||
"className": "", | ||
"x": 370, | ||
"y": 60, | ||
"wires": [ | ||
[ | ||
"fa633410a8a67c23" | ||
] | ||
] | ||
}, | ||
{ | ||
"id": "fa633410a8a67c23", | ||
"type": "ui-chart", | ||
"z": "a725245cfaf96f83", | ||
"group": "32fcb041e36de17e", | ||
"name": "", | ||
"label": "chart", | ||
"order": 6, | ||
"chartType": "line", | ||
"category": "topic", | ||
"categoryType": "msg", | ||
"xAxisProperty": "", | ||
"xAxisPropertyType": "msg", | ||
"xAxisType": "time", | ||
"yAxisProperty": "", | ||
"ymin": "", | ||
"ymax": "", | ||
"showLegend": true, | ||
"removeOlder": 1, | ||
"removeOlderUnit": "3600", | ||
"removeOlderPoints": "", | ||
"colors": [ | ||
"#1f77b4", | ||
"#aec7e8", | ||
"#ff7f0e", | ||
"#2ca02c", | ||
"#98df8a", | ||
"#d62728", | ||
"#ff9896", | ||
"#9467bd", | ||
"#c5b0d5" | ||
], | ||
"width": 6, | ||
"height": 8, | ||
"className": "", | ||
"x": 550, | ||
"y": 120, | ||
"wires": [ | ||
[] | ||
] | ||
}, | ||
{ | ||
"id": "8a9f5a7b055210a9", | ||
"type": "change", | ||
"z": "a725245cfaf96f83", | ||
"name": "Remove _client", | ||
"rules": [ | ||
{ | ||
"t": "delete", | ||
"p": "_client", | ||
"pt": "msg" | ||
} | ||
], | ||
"action": "", | ||
"property": "", | ||
"from": "", | ||
"to": "", | ||
"reg": false, | ||
"x": 360, | ||
"y": 120, | ||
"wires": [ | ||
[ | ||
"fa633410a8a67c23" | ||
] | ||
] | ||
}, | ||
{ | ||
"id": "8934d174f3845df7", | ||
"type": "ui-slider", | ||
"z": "a725245cfaf96f83", | ||
"group": "32fcb041e36de17e", | ||
"name": "", | ||
"label": "Send to All Users", | ||
"tooltip": "", | ||
"order": 2, | ||
"width": 0, | ||
"height": 0, | ||
"passthru": false, | ||
"outs": "all", | ||
"topic": "topic", | ||
"topicType": "msg", | ||
"thumbLabel": true, | ||
"min": 0, | ||
"max": 10, | ||
"step": 1, | ||
"className": "", | ||
"x": 130, | ||
"y": 120, | ||
"wires": [ | ||
[ | ||
"8a9f5a7b055210a9" | ||
] | ||
] | ||
}, | ||
{ | ||
"id": "31b0d54757860d1b", | ||
"type": "ui-slider", | ||
"z": "a725245cfaf96f83", | ||
"group": "32fcb041e36de17e", | ||
"name": "Send to Same User", | ||
"label": "Send to Same User", | ||
"tooltip": "", | ||
"order": 3, | ||
"width": 0, | ||
"height": 0, | ||
"passthru": false, | ||
"outs": "all", | ||
"topic": "topic", | ||
"topicType": "msg", | ||
"thumbLabel": true, | ||
"min": 0, | ||
"max": 10, | ||
"step": 1, | ||
"className": "", | ||
"x": 130, | ||
"y": 180, | ||
"wires": [ | ||
[ | ||
"de0801fb73bdceb3" | ||
] | ||
] | ||
}, | ||
{ | ||
"id": "de0801fb73bdceb3", | ||
"type": "change", | ||
"z": "a725245cfaf96f83", | ||
"name": "Remove socketId", | ||
"rules": [ | ||
{ | ||
"t": "delete", | ||
"p": "_client.socketId", | ||
"pt": "msg" | ||
} | ||
], | ||
"action": "", | ||
"property": "", | ||
"from": "", | ||
"to": "", | ||
"reg": false, | ||
"x": 350, | ||
"y": 180, | ||
"wires": [ | ||
[ | ||
"fa633410a8a67c23" | ||
] | ||
] | ||
}, | ||
{ | ||
"id": "32fcb041e36de17e", | ||
"type": "ui-group", | ||
"name": "Group Name", | ||
"page": "e857a4a5a6b5320f", | ||
"width": "6", | ||
"height": "1", | ||
"order": 1, | ||
"showTitle": true, | ||
"className": "", | ||
"visible": true, | ||
"disabled": false | ||
}, | ||
{ | ||
"id": "e857a4a5a6b5320f", | ||
"type": "ui-page", | ||
"name": "Page Name", | ||
"ui": "c851adb9b2a29c9e", | ||
"path": "/", | ||
"icon": "", | ||
"layout": "grid", | ||
"theme": "35ee7753b5b3599b", | ||
"order": 3, | ||
"className": "", | ||
"visible": false, | ||
"disabled": false | ||
}, | ||
{ | ||
"id": "c851adb9b2a29c9e", | ||
"type": "ui-base", | ||
"name": "Multi User Dashboard", | ||
"path": "/dashboard", | ||
"includeClientData": true, | ||
"acceptsClientConfig": [ | ||
"ui-notification", | ||
"ui-control", | ||
"ui-template", | ||
"ui-chart" | ||
] | ||
}, | ||
{ | ||
"id": "35ee7753b5b3599b", | ||
"type": "ui-theme", | ||
"name": "Theme Name", | ||
"colors": { | ||
"surface": "#16234b", | ||
"primary": "#1d44b9", | ||
"bgPage": "#ecf2f8", | ||
"groupBg": "#ffffff", | ||
"groupOutline": "#cccccc" | ||
} | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<script setup> | ||
import { ref } from 'vue' | ||
import FlowViewer from '../components/FlowViewer.vue' | ||
import ExampleSlider from '../examples/multi-tenancy.json' | ||
|
||
const examples = ref({ | ||
'multi-tenant-slider': ExampleSlider | ||
}) | ||
</script> | ||
|
||
# Building Multi-Tenant Dashboards | ||
|
||
We have two fundamental design patterns when building a Dashboard: | ||
|
||
- **Single Source of Truth:** All users of your Dashboard will see the same data. This is useful for industrial IoT or Home Automation applications. | ||
- **Multi Tenancy:** Data shown in a particular widget is unique to a given client/session/user. This represents a more traditional web application, where each user has their own session and associated data. | ||
|
||
By default, any data passed to a Dashboard node is broadcast to all connected clients, and therefore visible to all users ("Single Source of Truth"). In Industrial IoT type use-cases, this is very useful where you may have a chart showing data about a given piece of equipment. | ||
|
||
In other cases though, you may wish to send data to just a single client/user, this is where multi-tenancy comes in, and allows you to define constraints on which clients receive particular data. | ||
|
||
You can read more about the design patterns [here](../getting-started.md#design-patterns). | ||
|
||
## Understanding Client Data | ||
|
||
With "Include Client Data" enabled, every `msg` a node emits will have a `_client` object appended to it. This object will detail any available information about the client/user interacting with a given Dashboard. | ||
|
||
### Core Client Data | ||
|
||
Out of the box, Dashboard will append two piece of information to the `_client` object: | ||
|
||
- `socketId`: The unique ID of the socket connection that the client is using to interact with the Dashboard. | ||
- `socketIp`: The IP address of the client interacting with the Dashboard. | ||
|
||
### Plugin Data Providers | ||
|
||
Plugins, such as the [FlowFuse User Addon](https://flowfuse.com/blog/2024/04/displaying-logged-in-users-on-dashboard/), are available to append additional information to the `_client` object. | ||
|
||
These Authentication plugins can be installed via the Node-RED Palette Manager, and often require Node-RED to be setup with a given Authentication provider, separately from Dashboard. For example, for the FlowFuse User Addon, it is a requirement for Node-RED to be running on FlowFuse with the ["FlowFuse User Authentication"](https://flowfuse.com/docs/user/instance-settings/#flowfuse-user-authentication) option enabled. | ||
|
||
The plugins will append additional information to the `_client` object, such as the `user` object, which details the user's name, email address, and any other information that the Authentication provider has available. | ||
|
||
## Configuring Client Data | ||
|
||
In the [Dashboard sidebar](./sidebar.md#client-data) within the Node-RED Editor, you will find the "Client Data" tab: | ||
|
||
<img data-zoomable style="max-width: 400px; margin: auto;" src="/images/dashboard-sidebar-clientdata.png" alt="Screenshot of an example 'Client Data' tab"/> | ||
<em>Screenshot of an example "Client Data" tab</em> | ||
|
||
Client data defines information on the user/client interacting with the Dashboard. This data can be appended to every `msg` a node emits, underneath teh `msg._client` object. | ||
|
||
When "Include Client Data" is enabled, every `msg._client` will detail the `socketId` and `socketIp` of any connected users. | ||
|
||
## Simple Example | ||
|
||
Here we demonstrate three different clients opening the same Dashboard. | ||
|
||
The Dashboard consists of three sliers and a chart, and is running on FlowFuse, with the "FlowFuse User Addon" plugin enabled. | ||
|
||
Two of the clients are logged in as the same user, and the third, "Another User". | ||
|
||
<video controls> | ||
<source src="https://github.com/FlowFuse/node-red-dashboard/assets/99246719/76601b4c-8d25-451c-b04f-e5ee4cf7efb0" type="video/mp4"> | ||
Your browser does not support the video tag. | ||
</video> | ||
|
||
We can see how it's possible to control the interaction of a widget, and how the data emitted from that widget is shared to other components and clients. | ||
|
||
The first, "Send to All Users" slider pass through a change node which removes the `_client` object from the message, meaning that the data is sent to all clients, as no constraints are defined. | ||
|
||
The second, "Send to Same User" slider passes through a change node, and has the `socketId` field removed from `msg._client`, leaving just the `user` object. This means the data is sent to any connected clients where the same authenticated user is found. | ||
|
||
Finally, the "Single Client" slider just passes it's default output to the "Chart" node, including the full `msg._client` object. This means that the data is only sent to the client (`socketId`) & user (`user`) that interacted with the slider. | ||
|
||
Below you will find the flow that runs the above example: | ||
|
||
<FlowViewer :flow="examples['multi-tenant-slider']" height="300px"/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters