Many web applications must restrict access to some features for some users for various reasons. For example: Only premium users get access to extra features. Only supervisors can edit online store prices. If your web app is a dynamically rendered JS, your frontend code needs to know what restrictions your users have in order to render the UI. How can you centralize the logic? Where in the application should you check for and enforce restrictions? Join me on a safari through the approaches, patterns, and techniques for handling the logic around user capabilities in client-side apps.
59. ▸ Client-side rendered apps,
▸ that use REST APIs to
load and save data asynchronously,
▸ and the server can tell the client details
about the authenticated user.
(Though, same ideas can apply to server-side rendered views)
60. IMPLEMENTATION CONSTRAINTS:
1. ONLY GRANT NECESSARY CAPABILITIES
2. UI MUST COMMUNICATE CAPABILITIES
3. USE ROLE-BASED ACCESS CONTROL
62. THE SERVER-SIDE MUST
ENFORCE THE RESTRICTIONS
← OR ELSE STUFF LIKE THIS IS POSSIBLE
63. THE SERVER-SIDE MUST
ENFORCE THE RESTRICTIONS,
AND THE CLIENT-SIDE MUST
REFLECT THE RESTRICTIONS
64. // Don’t do this
if ( "Junior Warehouse Clerk" in user.roles ||
"Warehouse Clerk" in user.roles ||
"Warehouse Manager" in user.roles ) {
// Show "View Orders" button ...
}
65. // Do it like this!
if ( "view orders" in user.capabilities ) {
// Show "View Orders" button ...
}
67. // Checking for one capability
if ( "view orders" in user.capabilities ) {
// Show "View Orders" button ...
}
68.
69. // Checking for more than one capability
if ( "view orders" in user.capabilities ||
"edit orders" in user.capabilities ||
"manage customers" in user.capabilities ) {
// Show drop down menu icon ...
}
70. [ THIS KIND OF ]
CAPABILITY CHECKING
IS ADDITIVE
71. can ( user, ["view orders"] )
// returns true if user can view orders
can ( user, ["action one", "action two", "action three"] )
// returns true if the user can do any of the actions
72. function can (user, requiredCapabilities) {
return requiredCapabilities.some(function (capability) {
return capability in user.capabilities
})
}
73. if ( can (user, ["do something"]) ) {
// ...
}
74. if ( can (user, ["do action", "do another action"]) ) {
// ...
}
83. # Use the same kind of “can” per route server-side
get "/_api/orders"
can ( user, "view orders" ) do
# ...
end
end
post "/_api/orders"
can ( user, "add orders" ) do
# ...
end
end