Skip to content

Commit

Permalink
Merge pull request #8 from amrhossamdev/advanced-login
Browse files Browse the repository at this point in the history
finish log-in & log-out
  • Loading branch information
amrhossamdev authored Sep 17, 2024
2 parents 8789603 + 638e40c commit 8b19692
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .idea/simple_app.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion app/assets/stylesheets/custom.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,18 @@ input {

.dropdown-menu.active {
display: block;
}
}

.checkbox {
margin-top: -10px;
margin-bottom: 10px;
span {
margin-left: 20px;
font-weight: normal;
}
}

#session_remember_me {
width: auto;
margin-left: 0;
}
3 changes: 2 additions & 1 deletion app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def create
user = User.find_by(email: params[:session][:email])
if user && user.authenticate(params[:session][:password])
reset_session
params[:session][:remember_me] == "1" ? remember(user) : forget(user)
log_in user
redirect_to user, notice: "You are now logged in!"
else
Expand All @@ -15,7 +16,7 @@ def create
end

def destroy
log_out
log_out if logged_in?
redirect_to root_url, notice: "You are now logged out!"
end
end
26 changes: 25 additions & 1 deletion app/helpers/sessions_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,42 @@ module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end

def log_out
forget(current_user)
session.delete(:user_id)
@current_user = nil
end

def current_user
@current_user ||= User.find_by(id: session[:user_id])
if session[:user_id]
@current_user ||= User.find_by(id: session[:user_id])
elsif cookies.signed[:user_id]
user = User.find_by(id: cookies.signed[:user_id])
if user && user.authenticated?(cookies[:remember_token])
log_in user
@current_user = user
end
end
end

# Return true if the user is logged in

def logged_in?
!current_user.nil?
end

# Remember user
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end

def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
end

22 changes: 22 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class User < ApplicationRecord
attr_accessor :remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
Expand All @@ -10,4 +11,25 @@ def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end

# Return a random token.

def User.new_token
SecureRandom.urlsafe_base64
end

# Remember user in the database
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end

def forget
update_attribute(:remember_digest, nil)
end

def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
end
5 changes: 5 additions & 0 deletions app/views/sessions/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :remember_me, class: "checkbox inline" do %>
<%= f.check_box :remember_me %>
<span>Remember me on this computer</span>
<% end %>
<%= f.submit "Log in", class: "btn btn-primary" %>
<% end %>

Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20240915202915_add_remember_digest_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddRememberDigestToUsers < ActiveRecord::Migration[7.2]
def change
add_column :users, :remember_digest, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions test/helpers/sessions_helper_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'test_helper'
class SessionsHelperTest < ActionView::TestCase

def setup
@user = users(:amr)
remember(@user)
end
test "current_user returns right user when session is nil" do
assert_equal @user, current_user
assert is_logged_in?
end
test "current_user returns nil when remember digest is wrong" do
@user.update_attribute(:remember_digest, User.digest(User.new_token))
assert_nil current_user
end
end
16 changes: 14 additions & 2 deletions test/integration/users_login_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,33 @@ def setup

test "login with valid information followed by logout" do
post login_path, params: { session: { email: @user.email,
password: 'password' } }
password: "password" } }
assert is_logged_in?
assert_redirected_to @user
follow_redirect!
assert_template 'users/show'
assert_template "users/show"
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
delete logout_path
assert_not is_logged_in?
assert_redirected_to root_url
# Simulate user clicking logout in a second window
delete logout_path
follow_redirect!
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(@user), count: 0
end

test "login with remembering" do
log_in_as(@user, remember_me: "1")
assert_not_nil cookies["remember_token"]
end

test "login without remembering" do
log_in_as(@user, remember_me: "0")
assert_nil cookies["remember_token"]
end

end
4 changes: 4 additions & 0 deletions test/models/user_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,8 @@ def setup
@user.password = @user.password_confirmation = "a" * 5
assert_not @user.valid?
end

test "authenticated? should return false for a user with nil digest" do
assert_not @user.authenticated?("")
end
end
12 changes: 11 additions & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,19 @@ class ActiveSupport::TestCase

# Add more helper methods to be used by all tests here...


# Return true if the user is logged in.
def is_logged_in?
!session[:user_id].nil?
end

# Log in as a user
def log_in_as(user)
session[:user_id] = user.id
end
end

class ActionDispatch::IntegrationTest
def log_in_as(user, foo: "password", remember_me: "1")
post login_path, params: { session: { email: user.email, password: foo, remember_me: remember_me } }
end
end

0 comments on commit 8b19692

Please sign in to comment.