Authorizing a Weblog

Defining user roles

What do we want our roles to be able to do?

What would this look like in the controller?

Destroy

Let's start with destroy, which only an Admin should be able to do.

A user should only be able to delete an article if:

We are going to pretend that we're doing test driven development here. You write the code how you want it to work, and then you write the code that makes it work. This can greatly help simplify your code! We are going to use methods, like .admin? before we've defined them.

def destroy
  @article = Article.find(params[:id])

  if @article.user == current_user || current_user.admin?
    @article.destroy
    redirect_to articles_path
  else
    redirect_to :root, alert: "You do not have permission to do that."
  end
end

Updating

What are the rules for changing an article?

A user should only be able to update an article if:

def update
  @article = Article.find(params[:id])

  if @article.user == current_user || current_user.editor? || current_user.admin?

  if (current_user.editor? || current_user.admin?) || @article.user == current_user
    if @article.update(article_params)
      redirect_to @article
    else
      render 'edit'
    end
  else
    redirect_to :root, alert: "You do not have permission to do that."
  end
end

Displaying in views

Now we only want to show the edit link if the user is an editor or an admin

<% if article.user == current_user || current_user.admin? || current_user.editor? %>
  <td><%= link_to 'Edit', edit_article_path(article) %></td>
<% end %>

But that sucks... That's a lot of code, and we would repeat that all over the place!

What about a HELPER?

module ArticlesHelper
  def can_edit_article?(article)
    article.user == current_user || current_user.admin? || current_user.editor?
  end
end

Now let's change our view

<% if can_edit_article?(article) %>
  <td><%= link_to 'Edit', edit_article_path(article) %></td>
<% end %>

Much better!

Controllers

Our controller code is still pretty WET though. How do we DRY it out?

We could include the helpers into our controller, which is ok

class ArticlesController < ApplicationController
  include ArticlesHelper

or we can use pundit to replace both controllers and views!

Pundit is pretty easy and can really DRY up your views and controllers. If you feel sufficiently advanced, you can try it out for your final project. Otherwise it might be best to stick with basic authorization!

Pundit: https://github.com/elabs/pundit

Model functionality

We have these extra methods on our model, editor?, writer? etc.

  1. rails generate migration AddRoleToUser role:integer
  2. check that the migration came out how we wanted
  3. But we always want our users to have a role, so we should make sure that it's set to a value. What's a user without a role? an invalid user!
  4. so add , default: 0 to the end of the add_column.
  5. Now that our migration is correct, we can rake db:migrate

Right now our roles are all just numbers, 0, 1, 2. That's easy to forget and hard to use. We will use an enum to add all of those cool question mark methods to our models.

  1. add to user.rb enum role: [:writer, :editor, :admin]
  2. If you look in the Rails console, you'll see user.writer? and user.editor?
  3. See that role defaults to 0, but when you ask for it from rails, it says "writer"

In this way we can create roles for our users that we can use as if they are English! We can even output them in the views. <p><%= user.role %></p> would output <p>writer</p>