1. Computing

Part 4: Integrating Devise

By

Part 4: Integrating Devise

Great, everything is functional, now let's integrate the authentication into our Rails application. This really just involves adding a few things to the view and using a before filter. Devise made a few helpers that makes this really easy. In your controllers and views, you can call the following methods to get information about the state of the user (whether they're logged in, what the username is, etc).

  • before_filter :authenticate_user! - This is a before filter, it runs before the controller is executed. The goal here is to ensure that a user is logged in before they can do things like create posts. If they're not, they'll be sent back and get an error message.
  • user_signed_in? - This will return true if the user is signed in, or false if they are not. This will be very useful for showing one part of a view (like a login form) if the user is not logged in, and another part (like a post submit form) if they are logged in.
  • current_user - This will return the User object for the current user. We'll need this when we create the Post object, so posts are not created without users owning them.
  • user_session - A session is a temporary data store. It's not persistent in the database (usually), and is used to preserve state of the application between requests. Usually you won't need to touch this.

Let's start by offering the sign up and sign in links if not signed in, and greeting the user if they are signed in. We'll do this in the application template in app/views/layouts/application.rb since we want it to show up everywhere. Right now we'll just stuff it at the bottom in a div, we'll move it later when we do the actual layout of the page. If you read the list above, this view code should be rather self-explanatory. So this is what the body of the app/views/layouts/application.rb file should now look like.


<body>
<div id="flash">
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
</div>

<div id="content">
<%= yield %>
</div>

<div id="login">
<% if user_signed_in? -%>
<ul>
<li>Hello <%= current_user.email %>!</li>
<li><%= link_to 'Submit link', new_post_path %></li>
<li><%= link_to 'Sign out', destroy_user_session_path, :method => :delete %></li>
</ul>
<% else -%>
<ul>
<li><%= link_to 'Sign up', new_user_registration_path %></li>
<li><%= link_to 'Sign in', new_user_session_path %></li>
</ul>
<% end -%>
</div>

</body>

It's pretty ugly right now, all crammed in at the bottom, but we'll fix that later. With these links you can sign up, sign in and sign out. You can also submit links when logged in. It only displays the link when you're logged in, but you can still technically submit links when you're not logged in. We need to fix this, as well as not allow anyone to destroy links that aren't theirs. So, open up app/controllers/posts_controller.rb. You'll see a whole host of actions auto-generated by the scaffold generator. What we'll need to do is first add a before_filter on the actions we want to protect. So add the line before_filter :authenticate_user!, :except => [:index, :show] to the top of the class. This will make sure you're logged in before you can do anything "interesting."

Next, we need to make sure created posts are not orphaned, so let's modify the new action. We simply need to assign to the user attribute when we create the post, like so.


 def new
   @post = Post.new
   @post.user = current_user

You may also need to delete the User field from app/views/posts/_form.html.erb. This is the post form partial, it's used whenever a form for a post is rendered.

And finally, we check that the current user is the user that owns a post before the post is allowed to be edited or destroyed. This is done by a separate check in the standard respond_to block. For example, here is the new update method.


 def update
   @post = Post.find(params[:id])

   respond_to do |format|
     if @post.user != current_user
       format.html { redirect_to @post, notice: 'That post is not yours to edit' }
       format.json { render json: @post.errors, status: :unprocessable_entity }
     elsif @post.update_attributes(params[:post])
       format.html { redirect_to @post, notice: 'Post was successfully updated.' }
       format.json { head :no_content }
     else
       format.html { render action: "edit" }
       format.json { render json: @post.errors, status: :unprocessable_entity }
     end
   end
 end

The first part of the if statement checking the post is owned by the current user is new. It'll redirect to the post and give you an error message should the user not own the post. Similarly, this is the new destory action.


 def destroy
   @post = Post.find(params[:id])
   
   if @post.user != current_user
     flash[:notice] = 'That post is not yours to destory'

     respond_to do |format|
       format.html { redirect_to :back }
       format.json { head :no_content }
     end
   else
     @post.destroy

     respond_to do |format|
       format.html { redirect_to posts_url }
       format.json { head :no_content }
     end
   end
 end

When adding authentication to your application, it's best to go through every action and think in what context should the action be allowed. It can be easy to miss something here and there, and simple mistakes can he wide-reaching consequences.

  1. About.com
  2. Computing
  3. Ruby
  4. Ruby on Rails
  5. Creating a Reddit Clone with Rails
  6. Part 4: Integrating Devise

©2014 About.com. All rights reserved.