Giter VIP home page Giter VIP logo

devise_roles_readme's Introduction

Devise with Roles

Learning Objectives

  1. Explain the use of role-based authorization with Devise.
  2. Design a set of roles to model a forum with different permission levels.
  3. Set up Devise roles to implement such a model.

Overview

Devise gives you basically everything you need to solve the problem of authentication. CanCanCan offers a simple authorization model. In this lesson, we're going to look at using the role pattern for authorization.

What's a role?

Let's say that our app is a message board. There are four kinds of users: guests, normal users, moderators, and administrators.

Here's how they're defined and what they can do:

  • guests can read posts
  • normal users can do everything guests can do. They can also create posts and edit their own posts.
  • moderators can do all that, and edit and delete the posts of other users.
  • administrators can do everything.

Roles are a way to express these different kinds of users within the User model, then use it for authorization. Devise allows us to authenticate WHO you are, and devise's roles allow us to say (given what KIND of user you are) what you are authorized to do.

Using roles

Let's look at how we might implement the schema described above.

First, we'll put an enum in our User model to keep track of the User's role:

class User < ActiveRecord::Base
  enum role: [:normal, :moderator, :admin]
end

ActiveRecord generates several helpful methods from this call. Given a User, we can ask,

user.admin?

And ActiveRecord will translate that into:

user.role == 2

As you can see, enums are stored as integers in the database. The list of allowable values, and their conversion into symbols, happens in Ruby.

What about guests, or users who are either not logged in or not signed up for our service at all? We can detect whether a User is written to the database by calling persisted?, which will catch user objects which have been created, perhaps filled out in a /signup route, but not yet saved and validated. These users should be guests.

Our User model updates like so,

class User
  enum role: [:normal, :moderator, :admin]
  def guest?
    persisted?
  end
end

Note: current_user.role will never return 'guest' with this approach, since being a guest is not technically a role.

Devise's current_user method will always return to us the user object for the currently logged in user. What happens if we call current_user.guest? and current_user is nil because nobody is currently signed in. We can address that like so:

class NilClass
  def guest?
    true
  end
end

This will cause current_user.guest? to return true if current_user is nil. It will also cause any nil value, even literally nil to respond to guest? with true, so be wary of overusing this approach. Another technique is to use ActiveSupport's try whenever we're using an object that might be nil:

if current_user.try(:admin?)
  # current user is not nil and is an admin
else
  # current user is nil or has a role that is not admin
end

This is perhaps cleaner, since it delegates the bizarre re-opening of NilClass to ActiveSupport#try, but it is a little bit less readable than this:

if current_user.admin?
  # current user is not nil and is an admin
else
  # current user is nil or has a role that is not admin
end

So use your discretion.

Using the pattern

You can use this pattern whether you're just doing authorization checks in your controllers, or using a framework like CanCanCan.

Using it bare in the controller,

class PostsController < ApplicationController
  def update
    @post = Post.find(params[:id])
    return head(:forbidden) unless current_user.admin? ||
       			       current_user.moderator? ||
			       current_user.try(:id) == @post.id
    @post.update(post_params)
  end
  # more down here
end

In a CanCanCan ability, we use the role (What type of user you are) to determine what you are authorized to do.

class Ability
  include CanCan::ability

  def initialize(user)
    user.can :read, Post
    return if user.guest?
    
    user.can :update, Post, {owner_id: user.id}
    return if user.normal?
    
    user.can :update, Post
    return if user.moderator?

    user.can :manage, Post if user.admin?
  end
end

Note how the flow of the initializer creates cascading permissions. Since our permissions model is cascading—each new level of user can do everything the previous level can do, and then more—we start at the least-privileged user and add in the permissions for each level, returning when we reach the user's permission level.

Conclusion

Roles sit somewhere between authorization and authentication. By pre-defining what type of user each user is, and what each type of user is authorized to do, we can use roles to say who is allowed to do what.

Resources

View Devise Roles on Learn.co and start learning to code for free.

View Devise Roles on Learn.co and start learning to code for free.

devise_roles_readme's People

Contributors

annjohn avatar blake41 avatar crwhitesides avatar franknowinski avatar mendelb avatar queerviolet avatar siamking avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.