Skip to content

Commit

Permalink
Finish log in log out
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagonbotelho committed May 31, 2015
1 parent bf76f9f commit 3e8a7ca
Show file tree
Hide file tree
Showing 21 changed files with 277 additions and 8 deletions.
1 change: 1 addition & 0 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
//
//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require turbolinks
//= require_tree .
3 changes: 3 additions & 0 deletions app/assets/javascripts/sessions.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
14 changes: 14 additions & 0 deletions app/assets/stylesheets/custom.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,17 @@ input {
margin-top: 45px;
@include box_sizing;
}

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

#session_remember_me {
width: auto;
mergin-left: 0;
}
3 changes: 3 additions & 0 deletions app/assets/stylesheets/sessions.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Place all the styles related to the Sessions controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include SessionsHelper
end
22 changes: 22 additions & 0 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class SessionsController < ApplicationController
def new
end

def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_to user
else
flash.now[:danger] = "Invalid password or username"
render 'new'
end

end

def destroy
log_out if logged_in?
redirect_to root_url
end
end
1 change: 1 addition & 0 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def new
def create
@user = User.new(user_params)
if @user.save
log_in @user
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
Expand Down
39 changes: 39 additions & 0 deletions app/helpers/sessions_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end

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

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

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

def logged_in?
!current_user.nil?
end
end
25 changes: 25 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class User < ActiveRecord::Base
attr_accessor :remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
before_save { self.email = self.email.downcase }
validates :name, presence: true, length: { maximum: 50 }
Expand All @@ -7,4 +8,28 @@ class User < ActiveRecord::Base
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }

def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST:
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end

def User.new_token
SecureRandom.urlsafe_base64
end

def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end

def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end

def forget
update_attribute(:remember_digest, nil)
end
end
19 changes: 18 additions & 1 deletion app/views/layouts/_header.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,24 @@
<ul class="nav navbar-nav navbar-right">
<li><%= link_to "Home", root_path %></li>
<li><%= link_to "Help", help_path %></li>
<li><%= link_to "Log in", '#' %></li>
<% if logged_in? %>
<li><%= link_to "Users", '#' %></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Account <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li>
<li><%= link_to "Settings", '#'%></li>
<li class="divider"></li>
<li>
<%= link_to "Log Out", logout_path, method: "delete" %>
</li>
</ul>
</li>
<% else %>
<li><%= link_to "Log In", login_path %></li>
<% end %>
</ul>
</nav>
</div>
Expand Down
24 changes: 24 additions & 0 deletions app/views/sessions/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<% provide(:title, "Log In") %>
<h1>Log In</h1>

<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(:session, url: login_path) do |f| %>

<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>

<%= 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 %>

<p>New User? <%= link_to "Sign Up now!", signup_path %></p>
</div>
</div>
15 changes: 10 additions & 5 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
Rails.application.routes.draw do
get 'sessions/new'

get 'users/new'

root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users

# The priority is based upon order of creation: first created -> highest priority.
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20150529213232_add_remember_digest_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddRememberDigestToUsers < ActiveRecord::Migration
def change
add_column :users, :remember_digest, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20150521115706) do
ActiveRecord::Schema.define(version: 20150529213232) do

create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
end

add_index "users", ["email"], name: "index_users_on_email", unique: true
Expand Down
9 changes: 9 additions & 0 deletions test/controllers/sessions_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require 'test_helper'

class SessionsControllerTest < ActionController::TestCase
test "should get new" do
get :new
assert_response :success
end

end
6 changes: 5 additions & 1 deletion test/fixtures/users.yml
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
#empty

michael:
name: Michael Example
email: [email protected]
password_digest: <%= User.digest('password') %>
20 changes: 20 additions & 0 deletions test/helpers/sessions_helper_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require 'test_helper'

class SessionsHelperTest < ActionView::TestCase

def setup
@user = users(:michael)
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
49 changes: 49 additions & 0 deletions test/integration/users_login_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'test_helper'

class UsersLoginTest < ActionDispatch::IntegrationTest

def setup
@user = users(:michael)
end

test "login with valid information and logout" do
get login_path
post login_path, session: {email: @user.email, password: 'password'}
assert is_logged_in?
assert_redirected_to @user
follow_redirect!
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
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 invalid information" do
get login_path
assert_template 'sessions/new'
post login_path, session: { email: "", password: "" }
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
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
1 change: 1 addition & 0 deletions test/integration/users_signup_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class UsersSignupTest < ActionDispatch::IntegrationTest
password_confirmation: "password"}
end
assert_template 'users/show'
assert is_logged_in?
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 @@ -51,4 +51,8 @@ def setup
assert_not @user.valid?
end

test "authenticated? should return false fora user with nil digest" do
assert_not @user.authenticated?('')
end

end
21 changes: 21 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,25 @@ class ActiveSupport::TestCase
fixtures :all

# Add more helper methods to be used by all tests here...
def is_logged_in?
!session[:user_id].nil?
end

def log_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
if integration_test?
post login_path, session: { email: user.email,
password: password,
remember_me: remember_me }
else
session[:user_id] = user.id
end
end

private

def integration_test?
defined?(post_via_redirect)
end
end

0 comments on commit 3e8a7ca

Please sign in to comment.