Unless you're building an application entirely for your own use - or an application where all data is created anonymously and globally visible - at some point, you will need to think about users. In fact, when designing an application, we spend time upfront thinking about the user's perspective when we write user stories - i.e. how will a user experience and navigate the application.
Users are, in some ways, just like any other resource in an application with similar CRUD behavior. So let's start there.
Let's open up a new Rails application on Gitpod so we can get coding. Head over to https://github.com/entr451-spring2024/auth and "Use this template" to create a new repository in your GitHub account – name it auth
. Then, open the code in Gitpod.
Users
This application picks up where we left off in the previous lesson - CRUD functionality for the company
and contact
resources. There is a bit of new functionality as well. When viewing the show page of a contact, you can create an activity. You can find the code for this new functionality in app/views/contacts/show.html.erb
and /app/controllers/activities_controller.rb
.
If you think back to our domain modeling unit, our CRM application had the following user stories (and a few others, but these are relevant for now):
- As a salesperson, I want to manage a list of companies (name and the industries they belong to), so I know all the companies we are selling to and to categorize them by industry.
- As a salesperson, I want to maintain a list of contacts (along with each contact's name, email address, and phone number), so I know how to reach each person.
- As a salesperson, I want to associate contacts with a company, so that I can get a company-wide view of my sales team's communication with all the people at a company.
- As a salesperson, I want to be able to log each activity I have with a contact (e.g. calls/emails, with a date/time it occurred and my notes), so I can keep a diary of all the communication I have with each person.
The user role in these stories is "salesperson", which is just a descriptive name for "user". When looking back at the CRM domain model, you can think about the Salesperson
entity as the User
entity. To get things going, we've added the user
resource to the app code (along with a few other resources) by generating its model, table, and controller.
Open up the schema and take a look at the users
table that was created. It has columns for first_name
, last_name
, email
and password
(and, don't forget, an id
column which isn't shown in the schema but every table has one). Nothing new here that we haven't already seen.
Next take a look at the users controller (app/controllers/users_controller.rb
) - again, we generated this for you. This code should also be familiar. There's a show
action that will display user details. And there are new
and create
actions to display a new user form and then receive the data from that form and insert it into the database. This isn't any different than the functionality for creating companies
or contacts
in the previous lesson. However, conceptually this is more than merely the C in CRUD for a user - it represents an almost ubiquitous user story in every application:
- As a visitor, I want to create my own user account with an email and password.
This is a very basic version of the user story, but you'll see some variant of it in almost every application. Sometimes account creation is referred to as registration or signup. Sometimes a username
is required instead of, or in addition to, an email
. Sometimes the user role has a different name than "visitor". Regardless, the expected behavior is the same - a non-user becomes a user by submitting some credentials that can later be used for identification.
Now that we've checked out some of the new code, let's test it out. From terminal, start your server (rails server
), then click "Open Browser" when you see the popup - if you don't see a popup, click on "Remote Explorer" in the left sidebar (the fifth icon from the top in the left-hand sidebar) and then click the "Globe" icon. You'll see the typical Rails landing page. Modify the url with /users/new
at the end.
What an incredible looking signup form! Obviously, this will need some frontend TLC to modernize its appearance, but it'll do for now.
Fill out the form and submit. You should be redirected to the user's show page (/users/1
). This is new behavior - in the past we've redirected back to an index. Look in the controller at the last line of the create
action:
redirect_to "/users/#{@user["id"]}"
You can pick any url to redirect to after creating the record. We don't have a list of users in this application (it doesn't really make sense to expose our users publicly), so we can't redirect to the index. We could redirect to /companies
but redirecting to the user's show page gives the user some feedback that they did signup successfully. To get there, we just build a url that looks like /users/1
where 1
is the id
of this new user record.
Looking at the new user record you just created, what isn't displayed?
Passwords
The password is missing. That's very intentional - when deciding what information to display on a resource's show page, you don't have to (and rarely) show everything. You'll need to decide how to handle sensitive information in your applications, but as we'll soon see, passwords should never be exposed.
That said, for educational purposes, let's expose ours temporarily so we can learn more about user security.
Open up the show page (app/views/users/show.html.erb
) and add another list item to the <dl>
element with the user's password. It should now look like this:
<dl>
<dt>First Name:</dt>
<dd><%= @user["first_name"] %></dd>
<dt>Last Name:</dt>
<dd><%= @user["last_name"] %></dd>
<dt>Email:</dt>
<dd><%= @user["email"] %></dd>
<dt>Password:</dt>
<dd><%= @user["password"] %></dd>
</dl>
After updating the code, refresh your browser and you should see the password you just used to signup with.
This is BAD! If you can expose the password in your code, then the password is wholly insecure - anyone with a terminal can discover any user's password. Just to prove it, stop your server and start the sqlite console:
sqlite3 db/development.rb
Once you see the sqlite prompt sqlite>
, write some SQL to read the data:
sqlite> SELECT * FROM users;
And there, in plain text, you'll see the password you just signed up with. To reiterate, if an employee has access to the server's terminal and/or read access to your database, they have access to all the user passwords! 😱
Exit sqlite (using .exit
) and restart your rails server (rails server
).
We'll come back to this issue of exposed passwords in a bit when we start fixing the security vulnerabilities, but for now we'll just put a pin in it. A user can signup with their email and password. These are our app's authentication credentials - meaning, the data a user will use to identify themselves when they login.
When dealing with user login functionality in a software application, there are generally 2 steps to consider: authentication and authorization.
Authentication refers to asking a user who they are and to verify that they are who they say they are. Authorization refers to a user's restrictions and permissions once they have successfully logged in - meaning what are they allowed to do within the application. Or more simply, authentication asks "who is using the application" and authorization asks "what can a user do within the application". We'll start with authentication (next post).