From 9f19d0e16d7920e07255c0fbe596c518d1aa415f Mon Sep 17 00:00:00 2001 From: shumakl Date: Thu, 3 Apr 2014 15:04:42 -0400 Subject: fix login with tokens belonging to sessions --- app/controllers/users_controller.rb | 1 - app/helpers/sessions_helper.rb | 31 ++++++++--------- app/models/session.rb | 39 ++++++++++++++++++++++ app/models/user.rb | 66 +++---------------------------------- 4 files changed, 57 insertions(+), 80 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 8a8b994..60857f1 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -27,7 +27,6 @@ class UsersController < ApplicationController # POST /users.json def create @user = User.new(user_params) - @user.groups = 0 respond_to do |format| if @user.save sign_in @user diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index 9be3abc..54e4b0d 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -1,19 +1,13 @@ module SessionsHelper - def sign_in(user) - #create a new remember token - remember_token = User.new_remember_token - #place token inside of the browser - cookies.permanent[:remember_token] = remember_token - #save the hashed token to the database - user.update_attribute(:remember_token, - User.hash(remember_token)) - #set the current user to be the given user - self.current_user = user - end + @session = Session.new(user: user) + raw_token = @session.create_token + @session.save # FIXME: error handling - # The curret_user=(user) is the conversion of self.current_user = user - def current_user=(user) + @token = Session.hash_token(raw_token) + cookies.permanent[:remember_token] = raw_token + + #set the current user to be the given user @current_user = user end @@ -22,8 +16,9 @@ module SessionsHelper # since the remember token is hashed, we need to hash the cookie # to find match the remember token def current_user - remember_token = User.hash(cookies[:remember_token]) - @current_user ||= User.find_by(remember_token: remember_token) + @token ||= Session.hash_token(cookies[:remember_token]) + @session ||= Session.find_by(token: @token) + @current_user ||= (@session.nil? ? nil : @session.user) end # checks if someone is currently signed in @@ -32,9 +27,11 @@ module SessionsHelper end def sign_out - current_user.update_attribute(:remember_token, User.hash(User.new_remember_token)) + if signed_in? + @session.destroy + end + @current_user = nil cookies.delete(:remember_token) - self.current_user = nil end # This is for anyone that cares about how long a user is signed diff --git a/app/models/session.rb b/app/models/session.rb index a5fd26e..f5e642b 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -1,3 +1,42 @@ class Session < ActiveRecord::Base belongs_to :user + + ## + # Create a random remember token for the user. This will be + # changed every time the user creates a new session. + # + # If you want this value, hang on to it; the raw value is + # discarded afterward. + # + # By changing the cookie every new session, any hijacked sessions + # (where the attacker steals a cookie to sign in as a certain + # user) will expire the next time the user signs back in. + # + # The random string is of length 16 composed of A-Z, a-z, 0-9 + # This is the browser's cookie value. + def create_token() + t = SecureRandom.urlsafe_base64 + self.token = Session.hash_token(t) + t + end + + ## + # Encrypt the remember token. + # This is the encrypted version of the cookie stored on + # the database. + # + # The reasoning for storing a hashed token is so that even if + # the database is compromised, the attacker won't be able to use + # the remember tokens to sign in. + def Session.hash_token(token) + # SHA-1 (Secure Hash Algorithm) is a US engineered hash + # function that produces a 20 byte hash value which typically + # forms a hexadecimal number 40 digits long. + # The reason I am not using the Bcrypt algorithm is because + # SHA-1 is much faster and I will be calling this on + # every page a user accesses. + # + # https://en.wikipedia.org/wiki/SHA-1 + Digest::SHA1.hexdigest(token.to_s) + end end diff --git a/app/models/user.rb b/app/models/user.rb index 85f1982..277d885 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,17 +2,14 @@ class User < ActiveRecord::Base has_and_belongs_to_many :tournaments_played, class_name: "Tournament", foreign_key: "player_id", join_table: "players_tournaments" has_and_belongs_to_many :tournaments_hosted, class_name: "Tournament", foreign_key: "host_id", join_table: "hosts_tournaments" has_and_belongs_to_many :teams + has_many :sessions before_save { self.email = email.downcase } before_save { self.user_name = user_name } - ## - # Rails looks for the create_remember_token and runs the method - # before anything else. - # - # This method cannot be called by a user since it is denoted - # as private. - before_create :create_remember_token + def after_initialize + self.permissions = 0 + end def in_group?(group) case group @@ -93,59 +90,4 @@ class User < ActiveRecord::Base has_secure_password validates :password, length: { minimum: 6 } - - ## - # Create a random remember token for the user. This will be - # changed every time the user creates a new session. - # - # By changing the cookie every new session, any hijacked sessions - # (where the attacker steals a cookie to sign in as a certain - # user) will expire the next time the user signs back in. - # - # The random string is of length 16 composed of A-Z, a-z, 0-9 - # This is the browser's cookie value. - def User.new_remember_token - SecureRandom.urlsafe_base64 - end - - ## - # Encrypt the remember token. - # This is the encrypted version of the cookie stored on - # the database. - # - # The reasoning for storing a hashed token is so that even if - # the database is compromised, the attacker won't be able to use - # the remember tokens to sign in. - def User.hash(token) - Digest::SHA1.hexdigest(token.to_s) - end - - ## - # SHA-1 (Secure Hash Algorithm) is a US engineered hash - # function that produces a 20 byte hash value which typically - # forms a hexadecimal number 40 digits long. - # The reason I am not using the Bcrypt algorithm is because - # SHA-1 is much faster and I will be calling this on - # every page a user accesses. - # - # https://en.wikipedia.org/wiki/SHA-1 - - - # Everything under private is hidden so you cannot call. - private - - ## - # Create_remember_token in order to ensure a user always has - # a remember token. - def create_remember_token - self.remember_token = User.hash(User.new_remember_token) - end - - ## - # In order to ensure that someone did not accidentally submit - # two accounts rapidly (which would throw off the validates - # for user_name and email), I added an index to the Users - # email and user_name in the database to ensure uniqueness - # This also gives and index to the user_name and email - # so finding a user SHOULD be easier for the database. end -- cgit v1.2.3-54-g00ecf