Valve
This tutorial covers aspects of Valve – deepstreamHub’s realtime permission language. You can specify Valve rules in the permission section of the dashboard or upload them via the HTTP API.
Oh dear...permissions! Permissions are always super-hard to explain. I've read tutorials using The Simpsons, The Fellowship of the Ring and even the Olsen Twins to explain concepts like "access groups" and "right-inheritance".
With deepstreamHub, things can be equally tricky at times - but for different reasons. deepstreamHub is a realtime data platform. And even its permissions can be - if you want them to be - shared with both clients and backend processes in realtime.
The good news is that deepstream makes realtime permissions extremely easy using a permission language called "Valve". This tutorial assumes that you already know your way around Valve. If you haven't come across it yet, make sure to read the Simple Valve and Advanced Valve tutorials first.
But hold on: Why would I want realtime permissions?
A lot of times you'll want the same set of permissions in two places:
-Within the platform to enforce the permission-rules within a trusted environment.
-On the client to provide instant validation and a defensive design that avoids user frustration by making forbidden options unavailable.
As permissions change - e.g. a user being kicked out of a chat-group or a trader no longer being allowed to buy a certain stock - you'd want those permissions not only to take effect immediatly, but also gently and gracefully remove the associated options from the client's UI - the exact moment they become unavailable.
The goal of this tutorial
This tutorial won't be using The Simpsons or any other metaphor. Instead, it will use colors (hurray!). Here's what we want to achieve:
there will be three users and one admin with individual credentials
there is one global color record that can be set by any user to red, green or blue
each user gets three buttons, one for each color. When clicked, they set the global color.
the admin user can decide which user is allowed to set the global color to which value
Any change to a user's permission needs to reflect on their GUI in realtime and needs to be enforcable by the server
Please note: You can find the code for this example on Github
Creating users
For this tutorial we'll create four users via the deepstreamHub dashboard, using email authentication. Our users will be userA, userB and userC@example.com as well as admin@example.com.
Each user will be configured with two identical bits of serverData and clientData, namely
{
"permissionRecord": "permissions/usera",
"role": "user"
}
Okay, let's quickly go through what we've done here
serverData is user-specific information that can be used within Valve rules to decide if a specific action is permitted. It is purely used within the platform and won't be exposed to the client.
clientData is user-specific data that will be sent to the client upon login. deepstreamHub will add an extra field id
with the user-id to this. clientData is passed as the second argument to the .login()
callback, e.g.
var ds = deepstream('wss://154.deepstreamhub.com?apiKey=xxx');
var loginData = {
type: 'email',
email: 'userA@example.com',
password: 'usera-pass'
}
ds.login( loginData, function( success, data ){
/* data will be
{
id: "1425f045-9b1d-4938-affc-5dbf60035c0e",
permissionRecord: "permissions/usera",
role: "user"
}
*/
});
role can be either 'user'
or 'admin'
. Admins will be shown the admin GUI on the client and are allowed to set a user's permissions. Users can set the global color - if they are permissioned to set it to a particular value.
permissionRecord is the name of a record that will contain the actual permission information in the following format:
{
red: true,
blue: true,
green: false
}
The permissions
Valve rules can be edited in the permissions section of the deepstreamHub dashboard. The section on records will look as follows
record:
"*":
create: true
write: true
read: true
delete: false
"global-color":
write: "_(user.data.permissionRecord)[data.color]"
"permissions/*":
write: "user.data.role === 'admin'"
Default permissions
Lets look at this file step by step: Our example won't use events, RPCs or presence, so we turn all these off. For records we generally allow everything, but turn off deletion as this features won't be used in our example.
Allowing users to set global color
Here we decide whether a user is allowed to set the global color to a specific value. user.data.permissionRecord
is the name of the permission-record specified in our serverData
section.
_()
is the cross-reference-function used to load a record's data into our permission rule. data.color
is the color value in the incoming data the user tries to write.
"global-color":
write: "_(user.data.permissionRecord)[data.color]"
Allowing only admins to set permissions
Next up we need to make sure that only admins can set a user's permission. Similar to before we check for every write to any of the permission records (permissions/usera
etc.) if the role of the user attempting the write is admin
.
"permissions/*":
write: "user.data.role === 'admin'"
To summarize
This mechanism is all we need to ensure the above permissions on both client and server. Everytime a user tries to write to the global-color
record, her permissions are checked accordingly. Everytime an admin enables or disables a permission, the associated button on the user's GUI is turned on or off.