RELS Part 3: User Authentication with Ruby on Rails, RSpec and Capybara

Welcome to part 3 of our tutorial series: Build your own Real Estate Listing Service with Ruby on Rails. In this part we take care about the different users that can use the service and the behavior driven development of user authentication.

You can use it as standalone tutorial, too. Make sure that your development system is configured like described in Part1 of this series.

Additional Gems

For this tutorial we need some additional gems. Please uncomment this line in our Gemfile:

1

# gem 'bcrypt-ruby', '~> 3.0.0'

and add to the test group the email_spec:

12345

group :testdo
...gem'email_spec'
...end

We need bcrypt to store encrypted passwords for our users and email_spec to test email delivery. We use bundle install to make them available for our application. Additionally, for the email_spec gem we have to add this lines to our spec_helper.rb file:

Thinking About User Roles

For a real estate listing service we need different types of users. In our last parts we focused on the visitor-user accessing public functions like search. For this and upcomming tutorials we need two more roles so that we have a set of roles like this:

Admin

Visitor

Real Estate Agent

The admin is the super user and owner of our RELS. He has access to a admin dashboard, can manage all users and properties. The visitor is the regular user. He wants to search for properties. The real estate agent is a user who can submit and manage his own properties.

User Authentication

We need a user authentication system to implement different functions for the given user roles. Only the visitor role doesn’t need any authentication because this role has only access to public functions. The user data for the admin we can create manually but for the agent we need a sign up process. In summary we need following functions:

Real estate agent sign up

Admin and Agent log in and log out

The sign up functions should validate the entered user data and send a activation email to the given email address. The agent can’t log in until he activated his account.

The admin and the agent can log in to their accounts by clicking a link on the start page. If they try to access a protected site they will be redirected to the log in page.

User Authorization

The admin and agent users are not allowed to use protected functions equally. Additionally to the public functions the agent user can do these actions:

Edit his own user data

Create, update or delete his own properties

The admin user has access to all data of the site. Until now he has access to these functions:

List, create, update or delete users

List, create, update or delete properties

Has access to the admin dashboard

To implement this access structure we use a role based authorization system making controller actions only accessible for certain roles.

User Management Features with RSpec and Capybara

As the site owner

I want to provide an user management

so that I can protect functions and grant access based on roles

This is the high level description of our user management system. We use Capybara scenarios to write our acceptance tests for this feature. For this we create following file:

To make them available we have to add this line to our spec_helper.rb inside the Rspec.configure block:

12345

RSpec.configuredo|config|
...config.include(UserHelper)
...end

Let’s go through the feature spec.

In the background block we create an @agent user. This will fail because there is no user model.

In the ‘Real estate agent sign up’ scenario we describe the needed steps for creating a user. We use only a small set of user attributes but there is no problem to add more attributes later. With clicking the submit button the user gets a notice that he got an email for activation. This scenario fails because there are no links for registration.

In the ‘Real estate agent activation’ scenario we describe the activation steps. For this we use the activate function in our helper file. This function visits the activation url with the activation code as argurment. This test fails too, because there is no path for activation.

In the ‘User log in’ and ‘User log out’ scenarios we check the authentication. Here we have to take care that we have to activate our @agent we created in the background block. Only active agents are allowed to log in. In the next scenario, ‘Inactive User are not able to login’, we check this.

Every agent has it’s own ‘office’ page. On this page the agent can manage his properties. This page we have to restrict in access to agents only. The next scenarios take care of it.

In the scenario ‘Agents have access to their office page’ we describe the way agents access to their page. Visitors are not allowed to access this page testing it in the next scenario ‘Visitors dont have access to any office’.

In the scenario ‘Users with no agent role dont have office access’ the test that only agent roles can access an office pages.

User Model Specs

We start making all this scenarios pass by adding a user model to our application. For this we use the rails generator:

it "is invalid if no activation code is generated"do
u = create(:user, :agent)
expect(u.activation_code).to_not be_nil end

end

In this spec we describe the model validation. We want name, email and password with confirmation as required fields. Im the last block we test the activation code generation. This spec fails and before we can make it pass we modify our user factory in order to get testable stubs for our suite.

We find the file already prepared in spec/factories/users.rb and modify it like this:

This code makes some validation tests pass. The has_secure_password entry is a helper from rails handling password and password_confirmation validation functions. For the activation code we generate a random string with alphanumeric characters.

Besides some tests pass the main blocker is the UserMailer. To resolve this we have to take care about the email generation.

User Activation Mailer

We create the user mailer with the following command:

1

rails g mailer UserMailer

The generator creates all needed files. So we can open the spec/mailers/user_mailer_spec.rb and write tests like this:

In this spec we create an agent user and an email first. We test only two things. At first we test that the email will be send to the correct email address. At second we test that the activation link is inside. Because we can the second test only make pass if we activation function works we set this test on pending.

The first test fails because there is no confirmation_email. To make this test pass we open the mailers/user_mailer.rb and change the code that it looks like this:

After user creation we redirect to the homepage with a flash notice. Our registration process works now and the matching scenario should pass. After the user sign up we have to make our activation functions pass. For this we add the following function to our users_controller:

This function searches for a user with the matching activation code. For activating we set the activation code to nil. After saving the user we redirect to our homepage with a flash notice. To use this activate function we have to add following to our routes.rb:

1

get "activate/:code"=>"users#activate", :as=>"activate"

To get our flash message output displayed we open our application.html.erb and change the code that it looks like this:

Now the user feature scenario ‘Real estate agent activation’ should pass. Go back to the mailer spec and uncomment the pending test and create the email view views/user_mailer/ confirmation_email.text.erb. Insert the following code to the view:

It handles multiple functions. At first we use the rails helper functions to find a a user with the entered email and password
combination. If this user exists we check for activation_code via is_active? function. If the user is active we store the user_id in the session container and redirect to the target_url or the root_path. The target_url is used if the user has tried to call a protected site directly. After authorization we want to sent the use back to his called site.

With this function the log in scenario passes. Now we take care about the log out test. For this we add to our sessions controller following function:

The current_user function is a helper function it returns the user with the help of the session stored user_id. It returns the user if the user is logged in or returns nil if no session user_id exists. To use this function sitewide we add

This function above we use if we want to protect a controller or single actions of a controller. This function asks for a logged user. If no user is logged in it redirects to our login form with a flash message. It’s the function for authentication.

This function we use to limit the access to certain roles. In each action we want to protect we can use the following syntax to limit the access:

1

access_only_with_roles("agent","office")

Next we use this functions in our office controller. Now the controller should look like this:

12345678

class OfficesController < ApplicationController

defshow
auth_required
access_only_with_roles("agent")end

end

After we secured the office controller our controller spec starts failing. In this case just remove the spec because for now the behavior is covered by the feature specs. Next we open our views/offices/show.html.erb and modify the code that it looks like this:

It simply adds a headline to show us that we are in the private office area. If we save this file an rerun our test suite all tests should pass. Your console output should look like this:

123456

.............................

Finishedin5.43 seconds29 examples, 0 failures

Randomized with seed 29763

Summary

In this tutorial we built a basic user authentication and authorization system from scratch. We used the behavior driven development approach to develop the functions for this system. Some functions are still missing like password recovery and remember me cookies.

In the next tutorials we are going to extend the property model(pictures, texts, geodata) and we going to build the CRUD functions for the real estate agents.

If you like this tutorial please share it on your social networks. If you have tips for improvement or any other information please leave a comment.

I tried to follow along the series and had no trouble with the first two installments. However with this one I have three failing test.

1. The email body is empty.
2. The “Users with no agent role dont have office access” doesn’t contain the flash message. Yes, I added to the view.
3. The “Account inactive. Please activate your account.” test also doesnẗ contain the flash message but I’m not sure if that is due to number 2.

You are right and I updated the tutorial. I forgot to transfer the two code blocks from my app to the tutorial(confirmation.text.erb email view and update of application.html.erb)

I’m not sure with the idea to make the source code public accessible because I’m afraid of a lot support requests from copy&paste users with problems to get it running or with problems to get it deployed to provider XYZ.

Hi Emily, I’m currently working on part 4, but at the moment there is a lot of client work. I have to meet some deadlines before summer vacations. I will do my best do finish part 4 in the next two weeks.