Iteration J1: Adding Users

Let’s start by creating a model and database table to hold our administrators’ usernames and passwords. Rather than store passwords in plain text, we’ll store a digest hash value of the password. By doing so, we ensure that even if our database is compromised, the hash won’t reveal the original password, so it can’t be used to log in as this user using the forms:

 depot>​​ ​​bin/rails​​ ​​generate​​ ​​scaffold​​ ​​User​​ ​​name:string​​ ​​password:digest

We declare the password as a digest type, which is another one of the nice extra touches that Rails provides. Now run the migration as usual:

 depot>​​ ​​bin/rails​​ ​​db:migrate

Next we have to flesh out the user model:

 class​ User < ApplicationRecord
» validates ​:name​, ​presence: ​​true​, ​uniqueness: ​​true
  has_secure_password
 end

We check that the name is present and unique (that is, no two users can have the same name in the database).

Then there’s the mysterious has_secure_password.

You know those forms that prompt you to enter a password and then make you reenter it in a separate field so they can validate that you typed what you thought you typed? That’s exactly what has_secure_password does for you: it tells Rails to validate that the two passwords match. This line was added for you because you specified password:digest when you generated your scaffold.

The next step is to uncomment the bcrypt-ruby gem in your Gemfile:

 # Use Active Model has_secure_password
»gem ​'bcrypt'​, ​'~> 3.1.7'

Next, you need to install the gem:

 depot>​​ ​​bundle​​ ​​install

Finally, you need to restart your server.

With this code in place, we have the ability to present both a password and a password confirmation field in a form, as well as the ability to authenticate a user, given a name and a password.

Administering Our Users

In addition to the model and table we set up, we already have some scaffolding generated to administer the model. Let’s go through it and make some tweaks as necessary.

We start with the controller. It defines the standard methods: index, show, new, edit, create, update, and delete. By default, Rails omits the unintelligible password hash from the view. This means that in the case of users, there isn’t much to show, except a name. So, let’s avoid the redirect to showing the user after a create operation. Instead, let’s redirect to the user’s index and add the username to the flash notice:

 def​ ​create
  @user = User.​new​(user_params)
 
  respond_to ​do​ |format|
 if​ @user.​save
» format.​html​ { redirect_to users_url,
»notice: ​​"User ​​#{​@user.​name​​}​​ was successfully created."​ }
  format.​json​ { render ​:show​, ​status: :created​, ​location: ​@user }
 else
  format.​html​ { render ​:new​ }
  format.​json​ { render ​json: ​@user.​errors​,
 status: :unprocessable_entity​ }
 end
 end
 end

Let’s do the same for an update operation:

 def​ ​update
  respond_to ​do​ |format|
 if​ @user.​update​(user_params)
» format.​html​ { redirect_to users_url,
»notice: ​​"User ​​#{​@user.​name​​}​​ was successfully updated."​ }
  format.​json​ { render ​:show​, ​status: :ok​, ​location: ​@user }
 else
  format.​html​ { render ​:edit​ }
  format.​json​ { render ​json: ​@user.​errors​,
 status: :unprocessable_entity​ }
 end
 end
 end

While we are here, let’s also order the users returned in the index by name:

 def​ ​index
» @users = User.​order​(​:name​)
 end

Now that the controller changes are done, let’s attend to the view. We need to update the form used both to create a new user and to update an existing user. Note this form is already set up to show the password and password confirmation fields. We’ll make a few aesthetic changes so the form looks nice and matches the look and feel of the site.

»<div class=​"depot_form"​>
»
 <%=​ form_with(​model: ​user, ​local: ​​true​) ​do​ |form| ​%>
 <%​ ​if​ user.​errors​.​any?​ ​%>
  <div id=​"error_explanation"​>
  <h2>​<%=​ pluralize(user.​errors​.​count​, ​"error"​) ​%>
  prohibited this user from being saved:</h2>
 
  <ul>
 <%​ user.​errors​.​full_messages​.​each​ ​do​ |message| ​%>
  <li>​<%=​ message ​%>​</li>
 <%​ ​end​ ​%>
  </ul>
  </div>
 <%​ ​end​ ​%>
 
» <h2>Enter User Details</h2>
»
  <div class=​"field"​>
»<%=​ form.​label​ ​:name​, ​'Name:'​ ​%>
»<%=​ form.​text_field​ ​:name​, ​size: ​40 ​%>
  </div>
 
  <div class=​"field"​>
»<%=​ form.​label​ ​:password​, ​'Password:'​ ​%>
»<%=​ form.​password_field​ ​:password​, ​size: ​40 ​%>
  </div>
 
  <div class=​"field"​>
»<%=​ form.​label​ ​:password_confirmation​, ​'Confirm:'​ ​%>
 
»<%=​ form.​password_field​ ​:password_confirmation​,
»id: :user_password_confirmation​,
»size: ​40 ​%>
 
  </div>
 
  <div class=​"actions"​>
 <%=​ form.​submit​ ​%>
  </div>
 <%​ ​end​ ​%>
»
»</div>

Let’s try it. Navigate to http://localhost:3000/users/new. For a stunning example of page design, see the following screenshot.

images/r_1_new_user.png

After Create User is clicked, the index is redisplayed with a cheery flash notice. If we look in our database, you’ll see that we’ve stored the user details:

 depot>​​ ​​sqlite3​​ ​​-line​​ ​​db/development.sqlite3​​ ​​"select * from users"
  id = 1
  name = dave
 password_digest = $2a$10$lki6/oAcOW4AWg4A0e0T8uxtri2Zx5g9taBXrd4mDSDVl3rQRWRNi
  created_at = 2016-01-29 14:40:06.230622
  updated_at = 2016-01-29 14:40:06.230622

As we’ve done before, we need to update our tests to reflect the validation and redirection changes we’ve made. First we update the test for the create method:

  test ​"should create user"​ ​do
  assert_difference(​'User.count'​) ​do
» post users_url, ​params: ​{ ​user: ​{ ​name: ​​'sam'​,
»password: ​​'secret'​, ​password_confirmation: ​​'secret'​ } }
 end
 
» assert_redirected_to users_url
 end

Because the redirect on the update method changed too, the update test also needs to change:

  test ​"should update user"​ ​do
  patch user_url(@user), ​params: ​{ ​user: ​{ ​name: ​@user.​name​,
 password: ​​'secret'​, ​password_confirmation: ​​'secret'​ } }
» assert_redirected_to users_url
 end

We need to update the test fixtures to ensure there are no duplicate names:

 # Read about fixtures at
 # https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
 
 one:
» name: ​dave
  password_digest: ​<%= BCrypt::Password.create('secret') %>
 
 two:
» name: ​susannah
  password_digest: ​<%= BCrypt::Password.create('secret') %>

Note the use of dynamically computed values in the fixture, specifically for the value of password_digest. This code was also inserted by the scaffolding command and uses the same function that Rails uses to compute the password.[78]

At this point, we can administer our users; we need to first authenticate users and then restrict administrative functions so they’ll be accessible only by administrators.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.130.130