11: Adding User Authentication




Learning Rails show

Summary: <h2>Goals</h2> <p>In this lesson, we add user authentication so only logged-in users can access the page controller and modify the contents of the site.</p> <h2>Setup</h2> <p>We begin with the code with which we ended Lesson 10. These zip files contain the beginning and ending states of the code:</p> <ul> <li><a href="/learningrails_10.zip">Learning Rails example app code as of the end of Lesson 10</a></li> <li><a href="/learningrails_11.zip">Learning Rails example app code as of the end of Lesson 11</a></li> </ul> <h2>Page title cleanup</h2> <p>First, we need to take care of a little problem we created for ourselves with the way we handled the page title metadata in the previous lesson.</p> <p>We’re going to add a couple more admin controllers in this lesson, and with the approach used in the previous lesson, we’d need to specifically set the @pagetitle instance variable in each of those controllers. For now, we’re going to replace the existing setting of the title tag in the application layout with the following:</p> <pre> &lt;title&gt;&lt;%= @pagetitle || (@page &amp;&amp; @page.title) || "Learning Rails Sample Application" %&gt;&lt;/title&gt; </pre> <p>This line sets the pagetitle in one of three ways:</p> <ol> <li>If there is an instance variable @pagetitle, use that</li> <li>If not, then use @page.title, if there is a @page object</li> <li>If there’s neither a @pagetitle nor a @page object, use the fixed string</li> </ol> <p>This is a little messy, and in a later lesson we’ll fix it in a more elegant way.</p> <h2>Install plugin</h2> <p>The plugin we’re going to use is called restful_authentication. You can find plugins by searching on Google or on various Rails plugin directory sites. One good site is <a href="http://www.agilewebdevelopment.com">AgileWebDevelopment</a>. From this site, you can get the <span class="caps">URL</span> for the repository where the plugin code is located.</p> <p>To install restful_authentication, enter the following line in a terminal at the root of your application:</p> <pre> script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/ </pre> <p>You can view the readme in vendor/plugins/restful_authentication to learn more about it.</p> <h2>Set up the plugin</h2> <p>This plugin provides a generator of its own to help us set things up, so let’s run it:</p> <pre> script/generate authenticated user sessions </pre> <p>In this command, “authenticated” is the name of the generator; “user” is the name of the model that will store user names and passwords; and “sessions” is the name of the controller that will manage user sessions.</p> <p>This generator creates a migration for us, but we need to run the migration:</p> <pre> rake db:migrate </pre> <p>Finally, to make the methods provided by this plugin available in your controllers, you need to add the following line to the file controllers/application.rb:</p> <pre> include AuthenticatedSystem</pre> <p>The application controller is “mixed in” to all other controllers, so any code you put here is available to all controllers. So by putting the “include” statement for the authenticated system here, you’re effectively adding that code to every controller.</p> <h2>Creating a user</h2> <p>Before we can have log-in, we need to have users, so let’s add some user management.</p> <p>The authenticated generator we ran in the previous section gave us a users controller and a view for creating users, so browse to //localhost:3000/users/new and create a login for yourself.</p> <p>The generator also created a sessions controller, which is what handles the actual log in and log out operations. Logging in is creating a new session, and logging out is destroying a session. It’s convenient to have shortcuts we can use to refer to these, so let’s add some named routes. Add the following lines to the top of the config/routes.rb file:</p> <pre> map.logout '/logout', :controller =&gt; 'sessions', :action =&gt; 'destroy' map.login '/login', :controller =&gt; 'sessions', :action =&gt; 'new' </pre> <p>This gives us two things. The text ‘/logout’ and ‘/login’ makes those simple URLs work. And the words after the “map.” provide shortcuts we can refer to anywhere in our code when we want to generate that <span class="caps">URL</span>, using either login_path, for example, to generate a relative <span class="caps">URL</span>, or login_url, to generate a full, absolute <span class="caps">URL</span>.</p> <p>Let’s add a log-in button to the navigation bar. Open views/layouts/application.html.erb, and add this code to the end of the list of links that creates the nav bar:</p> <pre> &lt;li&gt; &lt;% if logged_in? %&gt; &lt;%= link_to "Log Out", logout_path %&gt; &lt;% else %&gt; &lt;%= link_to "Log In", login_path %&gt; &lt;% end %&gt; &lt;/li&gt; </pre> <p>The restful_authentication plugin provides us with a “logged_in?” method that we can use to determine if any user is logged in. If someone is logged in, then we display a log out link; if not, we display a log in link.</p> <h2>Using authentication to protect the admin pages</h2> <p>Now we have our entire authentication system in place, but we haven’t actually used it to require login for any pages. We need to invoke the authentication feature for the pages for which we want to require log-in.</p> <p>Open the file controllers/pages_controller.rb, and add the following line:</p> <pre> before_filter :login_required </pre> <p>“login_required” is another method provided for us by the restful_authentication plugin. The before_filter causes this method to be executed before any action in the controller.</p> <p>Now do the same for users_controller.</p> <h2>Take care of page titles</h2> <p>Just as in the previous lesson, we need to set the @pagetitle instance variable in the user and session controllers, since these pages aren’t generated from page objects.</p> <h2>Wrapping up</h2> <p>Try accessing localhost:3000/pages, and you should be presented with a login screen. Log in with the credentials created in the previous step, and you should see the Log In button in the nav bar change to Log Out.</p> <p>Try creating a new user by accessing localhost:3000/users/new. You shouldn’t be able to create a new user without being logged in. (If you didn’t create a user before adding the login requirement to the users controller, you can comment that line out temporarily.)</p> <h2>Coming up</h2> <p>In our next lesson, we’ll make a variety of small enhancements to add some polish and additional capabilities to our content management system.</p>