Login
- Basic idea:
- Enter a username and password in a form and submit.
- If the two match, put the user id in the session.
- Presence of the user id in the session indicates that the user is logged in.
- Absence of user id in session causes redirect to login (for most actions)
- We need a
Users
table to store (at minimum) username and (encrypted) password.bin/rails generate scaffold User name:string password:digest
- Notice the
digest
as a type- causes the hashed password to be stored.
- then it is not possible for anyone to see the password
- Notice
has_secure_password
in the model (depot_r/app/models/user.rb
)- Automatically includes code for several password-related activities
- If you attempt to create new user and password and confirm don’t match you get an error. This all happens behind the scenes.
- Look at
schema.rb
. User table haspassword_digest
.password
is automatically converted to thepassword_digest
when creating/updating a User.
- Need to include
bcrypt
inGemfile
- Show how the fixture is set up.
- We need a controller to set up sessions.
- This need not be a full CRUD setup
- In other words, we don’t want to run
rails generate scaffold
.- Scaffold generates more than we need.
- Just need
new
,create
, anddestroy
bin/rails generate controller Sessions new create destroy
- Basic idea:
- Send username and password
- Verify validity of username and password.
- Creating a new session is just putting the user id in the session
- show
Session#create
- Notice the
&
inuser&.authenticate
- “Safe navigation operator”
- short-cut for nested if statements.
- Especially useful in situations like
user&.address&.zip
. Can get anil
if any of the three are missing.
- show
Session#destroy
- Creating
new
,create
, anddestroy
with the generator also created views for each action.create
anddestroy
are unused.new
is where we can put the login form.- (
alert
in controller should benotice
)
- (
- Routing:
- By convention the route to
Session#new
would besession/new
. - But,
login
makes more sense. - So we can edit
routes.rb
- By convention the route to
controller :sessions do
get 'login' => :new
post 'login' => :create
delete 'logout' => :destroy
end
“Middleware”
- Code inserted in to the “middle” of a request.
- In this case, we want a way to check to see if the user is logged in before granting access to certain (most) actions
- look at
ApplicationController
andbefore_action
class ApplicationController < ActionController::Base
before_action :authorize
# ...
protected
def authorize
unless User.find_by(id: session[:user_id])
redirect_to login_url, notice: "Please log in"
end
end
end
- As written, this applies to every action (i.e., code executes before code for main action)
- not usually what we want. Common exceptions:
- root/home page
- “About”
- login (Think about the consequences of putting login behind a login!)
- not usually what we want. Common exceptions:
- Two approaches:
- Add authentication to every action that needs it
- “turn it off” for actions that don’t need it.
- Which is better and why?
- Better to have something locked and unlock it later than push to production with something important left unprotected.
- Look at
skip_before_action
in a couple controllers.
Transactions
- Group database actions together so that if any of them fail, they all get rolled back:
def transfer_money
ActiveRecord::Base.transaction do
john.update!(money: john.money + 100)
ted.update!(money: ted.money - 100)
end
end
- Notice the use of the
!
version of the methods.- Need an exception to be raised to trigger the rollback.
- https://medium.com/@kristenrogers.kr75/rails-transactions-the-complete-guide-7b5c00c604fc