Permalink
Browse files

I5

  • Loading branch information...
1 parent c4580b0 commit ffc78eab88bcd9aca48fd796ba3bd5adf031058b @steveklabnik steveklabnik committed Apr 15, 2012
Showing with 51 additions and 50 deletions.
  1. +51 −50 source/projects/blogger.markdown
@@ -1940,7 +1940,7 @@ Now that you've tried out three plugin libraries (Paperclip, HAML, and SASS), It
Authentication is an important part of almost any web application and there are several approaches to take. Thankfully some of these have been put together in plugins so we don't have to reinvent the wheel.
-The "flavor-of-the-week" is one named AuthLogic and I wrote up an iteration using it for the [JSMerchant](http://jumpstartlab.com/resources/rails-jumpstart/jsmerchant/) tutorial, but I think it is a little complicated for a Rails novice. You have to create several different models, controllers, and views manually. The documentation is kind of confusing, and I don't think my tutorial is that much better.
+There are two popular gems for authentication: One is one named AuthLogic and I wrote up an iteration using it for the [JSMerchant](http://jumpstartlab.com/resources/rails-jumpstart/jsmerchant/) tutorial, but I think it is a little complicated for a Rails novice. You have to create several different models, controllers, and views manually. The documentation is kind of confusing, and I don't think my tutorial is that much better. The second is called [Devise](https://github.com/plataformatec/devise), and while it's the gold standard for Rails 3 applications, it is also really complicated.
So, instead, we'll use a relatively recent addition to the world of Rails authentication options, [Sorcery](https://github.com/NoamB/sorcery), which is a lightweight and straightforward implementation that gives us a good balance of functionality and exposure to the interesting pieces and parts.
@@ -1976,23 +1976,23 @@ If it's there, you're ready to go!
This plugin makes it easy to get up an running by providing a generator that creates a model representing our user and the required data migrations to support authentication. Although Sorcery provides options to support nice features like session-based "remember me", automatic password-reset through email, and authentication against external services such as Twitter, we'll just run the default generator to allow simple login with a username and password.
-One small bit of customization we will do is to rename the default model created by Sorcery from "User" to "Blogger", which gives us a more domain-relevant name to work with. Run this from your terminal:
+One small bit of customization we will do is to rename the default model created by Sorcery from "User" to "Author", which gives us a more domain-relevant name to work with. Run this from your terminal:
```plain
-rails generate sorcery:install --model=Blogger
+rails generate sorcery:install --model=Author
```
Take a look at the output and you'll see roughly the following:
```plain
create config/initializers/sorcery.rb
gsub config/initializers/sorcery.rb
-generate model Blogger --skip-migration
+generate model Author --skip-migration
invoke active_record
- create app/models/blogger.rb
+ create app/models/author.rb
invoke rspec
- create spec/models/blogger_spec.rb
- insert app/models/blogger.rb
+ create spec/models/author_spec.rb
+ insert app/models/author.rb
create db/migrate/20120210184116_sorcery_core.rb
```
@@ -2002,7 +2002,7 @@ Let's look at the SorceryCore migration that the generator created before we mig
rake db:migrate
```
-Let's see what Sorcery created inside of the file `app/models/blogger.rb`:
+Let's see what Sorcery created inside of the file `app/models/author.rb`:
```ruby
class Blogger < ActiveRecord::Base
@@ -2016,7 +2016,7 @@ We can see it added a declaration of some kind indicating our Blogger class auth
It's annoying me that we keep going to `http://localhost:3000/` and seeing the Rails starter page. Let's make the root show our articles index page.
-First, delete the file `public/index.html`. Files in the public directory will take precedence over routes in our application, so as long as that file exists we can't route the root address anywhere.
+First, delete the file `public/index.html` if you haven't already. Files in the public directory will take precedence over routes in our application, so as long as that file exists we can't route the root address anywhere.
Second, open `config/routes.rb` and right above the other routes add in this one:
@@ -2035,12 +2035,12 @@ Though we could certainly drop into the Rails console to create our first user,
We don't have any CRUD support for our Blogger model, but we can quickly get it by generating a scaffold. The scaffold generator will want to overwrite some of the files we created when we generated the Sorcery files, so be sure to say no when it asks. To generate the scaffold, run the following:
```plain
-rails generate scaffold Blogger username:string email:string crypted_password:string salt:string
+rails generate scaffold Author username:string email:string crypted_password:string salt:string
```
-As usual, the command will have printed all generated files. In addition to not overwriting pre-existing files, we will also want to delete the migration that was created with the scaffold, which should look something like `db/migrate/20120213182537_create_bloggers.rb` but will have its own unique timestamp in the filename.
+As usual, the command will have printed all generated files. In addition to not overwriting pre-existing files, we will also want to delete the migration that was created with the scaffold, which should look something like `db/migrate/20120213182537_create_authors.rb` but will have its own unique timestamp in the filename.
-Now let's take a look at the form partial used for creating or editing Blogger records, found in `app/views/bloggers/_form.html.erb`, specifically at the form fields:
+Now let's take a look at the form partial used for creating or editing Author records, found in `app/views/authors/_form.html.erb`, specifically at the form fields:
```ruby
<div class="field">
@@ -2088,25 +2088,29 @@ We will want to remove the `crypted_password` and `salt` fields, because the end
</div>
```
-Now that we've updated our Blogger form we can open our the model file and add validation aroundthe password and password_confirmation fields. If the two do not match, we know our record should be invalid, else the user could have mistakenly set their password to something other than their attention.
+Now that we've updated our Blogger form we can open our the model file and add validation around the `password` and `password_confirmation` fields. If the two do not match, we know our record should be invalid, else the user could have mistakenly set their password to something other than their attention.
```ruby
-class Blogger < ActiveRecord::Base
+class Author < ActiveRecord::Base
authenticates_with_sorcery!
validates_confirmation_of :password, :message => "should match confirmation", :if => :password
end
```
The `password` and `password_confirmation` fields are sometimes referred to as "virtual attributes" because they are not actually being stored in the database. Instead, Sorcery uses the given password along with the automatically generated `salt` value to create and store the `crypted_password` value.
-With this in place, we can now go to `http://localhost:3000/bloggers/new` and we should see the new user form should popup. Let's enter in "admin" for the username, "admin@blogger.com" for email, and "password" for the password and password_confirmation fields, then click "Create Blogger". We should be taken to the show page for our new Blogger user.
+With this in place, we can now go to `http://localhost:3000/authors/new` and we should see the new user form should popup. Let's enter in "admin" for the username, "admin@example.com" for email, and "password" for the password and password_confirmation fields, then click "Create Author". We should be taken to the show page for our new Author user.
+
+Now it's displaying the hash and the salt here! Edit your `app/views/bloggers.show.html.erb` page to remove those from the display.
We can see that we've created a user record in the system, but we can't really tell if we're logged in. Sorcery provides a couple of methods for our views that can help us out: `current_user` and `logged_in?`. The `current_user` method will return the currently logged-in user if one exists and `false` otherwise, and `logged_in?` returns `true` if a user is logged in and `false` if not.
Let's open `app/views/layouts/application.html.haml` and add a little footer so the whole `%body%` chunk looks like this:
```ruby
%body
+ %p.flash
+ = flash[:message]
#container
#content
= yield
@@ -2122,16 +2126,16 @@ The go to `http://localhost:3000/articles/` and you should see "Logged out" on t
### Logging In
-How do we log in to our Blogger app? We can't yet! We need to build the actual endpoints for logging in and out, which means we need controller actions for them. We'll create a BloggerSessions controller and add in the necessary actions: new, create, and destroy.
+How do we log in to our Blogger app? We can't yet! We need to build the actual endpoints for logging in and out, which means we need controller actions for them. We'll create a AuthorSessions controller and add in the necessary actions: new, create, and destroy. In the file `app/controllers/author_sessions_controller.rb`:
```ruby
-class BloggerSessionsController < ApplicationController
+class AuthorSessionsController < ApplicationController
def new
- @blogger = Blogger.new
+ @author = Author.new
end
def create
- if @blogger = login(params[:username], params[:password])
+ if @author = login(params[:username], params[:password])
redirect_back_or_to(articles_path, :message => 'Logged in successfully.')
else
flash.now[:alert] = "Login failed."
@@ -2141,44 +2145,42 @@ class BloggerSessionsController < ApplicationController
def destroy
logout
- redirect_to(:bloggers, :message => 'Logged out!')
+ redirect_to(:authors, :message => 'Logged out!')
end
end
```
-As is common for Rails apps, the `new` action is responsible for rendering the related form, the `create` action accepts the submission of that form, and the `destroy` action removes a record of the appropriate type. In this case, our records are the BloggerSession objects that represent a logged-in user.
+As is common for Rails apps, the `new` action is responsible for rendering the related form, the `create` action accepts the submission of that form, and the `destroy` action removes a record of the appropriate type. In this case, our records are the Author objects that represent a logged-in user.
-Let's create the template for the `new` action that contains the login form, in `app/views/blogger_sessions/new.html.haml`:
+Let's create the template for the `new` action that contains the login form, in `app/views/author_sessions/new.html.haml`: (you may have to make the directory)
```ruby
-<h1>Login</h1>
+%h1 Login
-<%= form_tag blogger_sessions_path, :method => :post do %>
- <div class="field">
- <%= label_tag :username %><br />
- <%= text_field_tag :username %>
- </div>
- <div class="field">
- <%= label_tag :password %><br />
- <%= password_field_tag :password %>
- </div>
- <div class="actions">
- <%= submit_tag "Login" %>
- </div>
-<% end %>
+= form_tag author_sessions_path, :method => :post do
+ .field
+ = label_tag :username
+ = text_field_tag :username
+ %br/
+ .field
+ = label_tag :password
+ = password_field_tag :password
+ %br/
+ .actions
+ = submit_tag "Login"
-<%= link_to 'Back', articles_path %>
+= link_to 'Back', articles_path
```
The `create` action handles the logic for logging in, based on the parameters passed from the rendered form: username and password. If the login is successful, the user is redirected to the articles index, or if the user had been trying to access a restricted page, back to that page. If the login fails, we'll re-render the login form. The `destroy` action calls the `logout` method provided by Sorcery and then redirects.
Next we need some routes so we can access those actions from our browser. Open up `config/routes.rb` and make sure it includes the following:
```ruby
-resources :blogger_sessions
+resources :author_sessions
-match 'login' => 'blogger_sessions#new', :as => :login
-match 'logout' => 'blogger_sessions#destroy', :as => :logout
+match 'login' => 'author_sessions#new', :as => :login
+match 'logout' => 'author_sessions#destroy', :as => :logout
```
With the last two lines, we created the named routes helpers `login_path`/`login_url` and `logout_path`/`logout_url`. Now we can go back to our footer in `app/views/layouts/application.html.haml` and update it to include some links:
@@ -2210,13 +2212,13 @@ Let's add in a protection scheme like this to the new users form:
That way when the app is first setup we can create an account, then new users can only be created by a logged in user.
-We can create a `before_filter` which will run _before_ the `new` and `create` actions of our `bloggers_controller.rb`. Open that controller and put all this code:
+We can create a `before_filter` which will run _before_ the `new` and `create` actions of our `authors_controller.rb`. Open that controller and put all this code:
```ruby
- before_filter :zero_bloggers_or_authenticated, :only => [:new, :create]
+ before_filter :zero_authors_or_authenticated, :only => [:new, :create]
- def zero_bloggers_or_authenticated
- unless Blogger.count == 0 || current_user
+ def zero_authors_or_authenticated
+ unless Author.count == 0 || current_user
redirect_to root_path
return false
end
@@ -2228,7 +2230,7 @@ The first line declares that we want to run a before filter named `zero_bloggers
With that in place, try accessing `bloggers/new` when you logged in and when your logged out. If you want to test that it works when no users exist, try this at your console:
```plain
-Blogger.destroy_all
+Author.destroy_all
```
Then try to reach the registration form and it should work! Create yourself an account if you've destroyed it.
@@ -2237,15 +2239,15 @@ Then try to reach the registration form and it should work! Create yourself an
The first thing we need to do is sprinkle `before_filters` on most of our controllers:
-* In `bloggers_controller`, add a before filter to protect the actions besides `new` and `create` like this:<br/>`before_filter :require_login, :except => [:new, :create]`
+* In `authors_controller`, add a before filter to protect the actions besides `new` and `create` like this:<br/>`before_filter :require_login, :except => [:new, :create]`
* In `tags_controller`, we don't have any methods that need to be protected.
-* In `blogger_sessions_controller` all the methods need to be accessible to allow login and logout
+* In `author_sessions_controller` all the methods need to be accessible to allow login and logout
* In `comments_controller`, we never implemented `index` and `destroy`, but just in case we do let's allow unauthenticated users to only access `create`:<br/>`before_filter :require_login, :except => [:create]`
* In `articles_controller` authentication should be required for `new`, `create`, `edit`, `update` and `destroy`. Figure out how to write the before filter using either `:only` or `:except`
Now our app is pretty secure, but we should hide all those edit, destroy, and new article links from unauthenticated users.
-Open `app/views/articles/index.html.erb` and find the section where we output the "Actions". Wrap that whole section in an `if` clause like this:
+Open `app/views/articles/show.html.erb` and find the section where we output the "Actions". Wrap that whole section in an `if` clause like this:
```ruby
<% if logged_in? %>
@@ -2258,11 +2260,10 @@ Look at the article listing in your browser when you're logged out and make sure
If you look at the `show` view template, you'll see that we never added an edit link! Add that link now, but protect it to only show up when a user is logged in.
Your basic authentication is done, and Iteration 5 is complete!
-```
### Extra Credit
-We now have the concept of users, represented by our `Blogger` class, in our blogging application, and it's bloggers who are allowed to create and edit articles. What could be done to make the ownership of articles more explicit and secure, and how could we restrict articles to being edited only by their original owner?
+We now have the concept of users, represented by our `Author` class, in our blogging application, and it's authors who are allowed to create and edit articles. What could be done to make the ownership of articles more explicit and secure, and how could we restrict articles to being edited only by their original owner?
## I6: Extras

0 comments on commit ffc78ea

Please sign in to comment.