lions and tigers and handling user capabilities - tiffany conroy - codemotion milan 2014
TRANSCRIPT
GOOD USER CAPABILITY UXHELPS PEOPLE TO
1. AVOID MISTAKES2. UNDERSTAND THEIR CAPABILITIES
3. EASILY MANAGE PERMISSIONS
Following this principle limits the potential damageof any security breach, whether accidental or malicious.
INTERACTION DESIGN ASKS
HOW DOES THE USER:affect change?
understand the change?understand what they can change?
INTERACTION DESIGN ASKS
HOW DOES THE USER:affect change?
understand the change?→ understand what they can change? ←
SUBJECT: PERSONOBJECT: THING
OPERATION: ACTIONPERMISSION: GIVES CAPABILITY
TO A PERSON TO PERFORM AN ACTION ON A THING
GOOD USER CAPABILITY UXHELPS PEOPLE TO
1. AVOID MISTAKES2. UNDERSTAND THEIR CAPABILITIES
3. EASILY MANAGE PERMISSIONS
▸ 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)
IMPLEMENTATION CONSTRAINTS:1. ONLY GRANT NECESSARY CAPABILITIES2. UI MUST COMMUNICATE CAPABILITIES
3. USE ROLE-BASED ACCESS CONTROL
// 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 ...}
$.ajax( url: "/_api/me/capabilities", success: function (response) { user.capabilities = response.capabilities }})
// Checking for one capability
if ( "view orders" in user.capabilities ) {
// Show "View Orders" button ...
}
// 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 ...}
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
function can (user, requiredCapabilities) { return requiredCapabilities.some(function (capability) { return capability in user.capabilities })}
// in your view codemustache.render(template, { can: user.capabilities, // ...})
<!-- in your mustache template -->{{#can.viewOrders}} <a link=“/orders">View Orders</a>{{/can.viewOrders}}
<nav> {{#can.viewOrders}} <a link="/orders">View Orders</a> {{/can.viewOrders}} {{#can.createOrders}} <a link="/orders/new">Add Order</a> {{/can.createOrders}}</nav>
<!-- This doesn’t work -->{{#can.viewOrders}} {{#can.createOrders}} <nav> ... </nav> {{/can.createOrders}}{{/can.viewOrders}}
// Augment capabilities with view-specific ones
if ( can(user, ["view orders", "create orders"]) ) { user.capabilities.viewMenu = true;}
mustache.render(template, { can: user.capabilities, // ...})
<!-- Handlebar template containing a “can” block helper -->{{#can "viewOrders editOrder"}} <nav> ... </nav>{{/can}}
// Define the block helper ...function canBlockHelper (requiredCapabilities, options) { // ... return hasSomeCapabilities ? options.fn(this) : ""}// ... and register it as “can”Handlebars.registerHelper("can", canBlockHelper);
{{#can "viewOrders editOrder"}} <nav> {{#can "viewOrders"}} <a link="/orders">View Orders</a> {{/can}} {{#can "createOrders"}} <a link="/orders/new">Add Order</a> {{/can}} </nav>{{/can}}
# 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
IMPLEMENTATION CONSTRAINTS:1. ONLY GRANT NECESSARY CAPABILITIES2. UI MUST COMMUNICATE CAPABILITIES
3. USE ROLE-BASED ACCESS CONTROL
CONSIDER WHEN IMPLEMENTING:1. ENFORCE IN BOTH SERVER AND CLIENTBUT MAKE THE SERVER THE AUTHORITY
2. CHECK AGAINST CAPABILITIES, NOT ROLES