Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Lions and tigers and handling user capabilities

Lions and tigers and handling user capabilities

Many applications 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 product categories. I went on a hunt to gather patterns and techniques for handling the logic around user capabilities in client-side apps. Join me on a safari through the approaches, and I’ll tell you what I have learned.

A video of this talk as given at Nordic.js can be viewed here:
https://www.youtube.com/watch?v=_CgYWacSTmQ

Note: the code snippets are optimized for readability. Don't copy and paste them ;)

Tiffany Conroy

September 19, 2014
Tweet

More Decks by Tiffany Conroy

Other Decks in Programming

Transcript

  1. GOOD USER CAPABILITY UX HELPS PEOPLE TO 1. AVOID MISTAKES

    2. UNDERSTAND THEIR CAPABILITIES 3. EASILY MANAGE PERMISSIONS
  2. INTERACTION DESIGN ASKS HOW DOES THE USER: affect change? understand

    the change? understand what they can change?
  3. INTERACTION DESIGN ASKS HOW DOES THE USER: affect change? understand

    the change? → understand what they can change? ←
  4. GOOD USER CAPABILITY UX HELPS PEOPLE TO 1. AVOID MISTAKES

    2. UNDERSTAND THEIR CAPABILITIES 3. EASILY MANAGE PERMISSIONS
  5. ▸ 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)
  6. IMPLEMENTATION CONSTRAINTS: 1. ONLY GRANT NECESSARY CAPABILITIES 2. UI MUST

    COMMUNICATE CAPABILITIES 3. USE ROLE-BASED ACCESS CONTROL
  7. // 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 ... }
  8. // Do it like this! if ( "view orders" in

    user.capabilities ) { // Show "View Orders" button ... }
  9. // Checking for one capability if ( "view orders" in

    user.capabilities ) { // Show "View Orders" button ... }
  10. // 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 ... }
  11. 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
  12. // in your view code mustache.render(template, { can: user.capabilities, //

    ... }) <!-- in your mustache template --> {{#can.viewOrders}} <a link=“/orders">View Orders</a> {{/can.viewOrders}}
  13. // Augment capabilities with view-specific ones if ( can(user, ["view

    orders", "create orders"]) ) { user.capabilities.viewMenu = true; } mustache.render(template, { can: user.capabilities, // ... })
  14. <!-- 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);
  15. {{#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}}
  16. # 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
  17. IMPLEMENTATION CONSTRAINTS: 1. ONLY GRANT NECESSARY CAPABILITIES 2. UI MUST

    COMMUNICATE CAPABILITIES 3. USE ROLE-BASED ACCESS CONTROL
  18. CONSIDER WHEN IMPLEMENTING: 1. ENFORCE IN BOTH SERVER AND CLIENT

    BUT MAKE THE SERVER THE AUTHORITY 2. CHECK AGAINST CAPABILITIES, NOT ROLES