Rails Authentication user and password stored on different models - ruby-on-rails

I am working on a project, i need to have different type of users, some will be admins and others will be just users, i have a model for all users, and i have a model "login" where i would store an ecrypted password. The problem is, i create the user, and it saves on the database, but the model where the password should be stored is empty...
I tried to make assossiation on both user and login controller, and both models as well.
Ask me for the code you need to see and ill post it here.
this is my user.rb code
class User < ActiveRecord::Base
has_and_belongs_to_many :requisitions
has_many :historics
has_one :login
belongs_to :rank
belongs_to :sub_unit
belongs_to :user_type
#attr_accessor :password, :password_confirmation, :salt, :encrypted_password
validates :nim, :posto_id, :apelido, :nome, :telefone, :sub_un_id, :tipo_util_id, presence: true
validates :email, format: { with: /#/ }
validates :nim, uniqueness: true
validates :password, :confirmation => true
validates_length_of :password, :in => 6..20, :on => :create
# Encrypting calls for new user's password
before_save :encrypt_password
after_save :clear_password
# Authentication process for user's login
def match_password(login_password = '')
encrypted_password == BCrypt::Engine.hash_secret(login_password, salt)
end
def self.authenticate(nim_as_login = '', login_password = '')
user = User.find_by_nim(nim_as_login)
if user && user.match_password(login_password)
return user
else
return false
end
end
# Encrypting process for new user's password
private
def encrypt_password
if password.present?
self.salt = BCrypt::Engine.generate_salt
self.encrypted_password = BCrypt::Engine.hash_secret(password, salt)
end
end
private
def clear_password
self.password = nil
end
end
This is my login.rb code
class Login < ActiveRecord::Base
belongs_to :user
attr_accessor :password, :password_confirmation, :salt, :encrypted_password
#validating fields
validates :user_id, :password, presence: true
validates :password, confirmation: true
validates :password, length: { in: 6..30 }
# Encrypting calls for new user's password
before_save :encrypt_password
after_save :clear_password
# Authentication process for user's login
def match_password(login_password = '')
encrypted_password == BCrypt::Engine.hash_secret(login_password, salt)
end
def self.authenticate(nim_as_login = '', login_password = '')
user = User.find_by_nim(nim_as_login)
login = Login.find_by_user_id(user.id)
if login && login.match_password(login_password)
return login
else
return false
end
end
# Encrypting process for new user's password
private
def encrypt_password
if password.present?
self.salt = BCrypt::Engine.generate_salt
self.encrypted_password = BCrypt::Engine.hash_secret(password, salt)
end
end
private
def clear_password
self.password = nil
end
end
this is my users controller code
class UsersController < ApplicationController
before_filter :save_login_state, only: [:new, :create]
def new
#user = User.new
#ranks = Rank.all
#types = UserType.all
#unit = SubUnit.all
#logins = Login.all
#page_title = "LoginUser | Novo"
end
def create
#user = User.new(user_params, login_params)
#LoginsController(:password, :password_confirmation)
#ranks = Rank.all
#types = UserType.all
#unit = SubUnit.all
#logins = Login.all
if #user.save
flash[:notice] = "Bem vindo #{#user.apelido}, sua conta foi criada com sucesso!"
redirect_to sessions_path
else
#user.errors.full_messages.each do |e|
if e == "Nim has already been taken"
flash.now[:error] = "Este Utilizador já está registado!"
else
flash.now[:error] = "Corrija os campos do formulário!"
end
end
render 'new'
end
end
def show
#user = User.find(params[:id])
#ranks = Rank.all
#types = UserType.all
#unit = SubUnit.all
#logins = Login.all
end
private
def user_params
params.require(:user).permit(:id, :nim, :posto_id, :apelido, :nome, :telefone, :telemovel, :email, :sub_un_id, :tipo_util_id)
end
def login_params
params.require(Login).permit(:password, :password_confirmation, :user_id )
end
end
this is my login controller
class LoginsController < ApplicationController
def create
#login = Login.all
end
end
this is my sessions controller
class SessionsController < ApplicationController
before_filter :authenticate_user, only: [:home, :profile, :setting]
before_filter :save_login_state, only: [:login, :login_attempt]
def login
#page_title = "LoginUser | Entrar"
end
def login_attempt
authorized_user = User.authenticate(params[:nim_as_login], params[:login_password])
if authorized_user
session[:user_id] = authorized_user.id
flash[:notice] = "Benvindo de volta #{authorized_user.apelido}"
redirect_to user_path
else
flash.now[:error] = "Email ou palavra passe inválida!"
render 'sessions/login'
end
end
def home
end
def profile
end
def setting
end
def logout
session[:user_id] = nil
redirect_to sessions_path
end
end
My question is, can i store the encrypted password and the user on different tables?

Related

Why is current_user called on render in controller?

I'm getting the following error when trying access the log in method of my sessions controller:
JWT::DecodeError (Nil JSON web token):
lib/json_web_token.rb:11:in `decode'
app/helpers/sessions_helper.rb:15:in `current_user'
app/controllers/api/sessions_controller.rb:11:in `create'
If I comment out my render json: user in my controller response, all is good, except I need to respond with the user...Why on earth is the current_user method called on through line 11 of the sessions_controller.rb. Here's the relevant code:
lib/json_web_token.rb
require 'jwt'
class JsonWebToken
def self.encode(payload, expiration = 24.hours.from_now)
payload = payload.dup
payload['exp'] = expiration.to_i
JWT.encode(payload, Rails.application.secrets.json_web_token_secret)
end
def self.decode(token)
JWT.decode(token, Rails.application.secrets.json_web_token_secret).first
end
end
sessions_helper.rb
require 'json_web_token'
module SessionsHelper
def create_session(user)
session[:user_id] = user.id
end
def current_user
auth_token = request.headers["Authorization"]
if auth_token
auth_token = auth_token.split(" ").last
begin
decoded_token = JsonWebToken.decode auth_token
rescue JWT::ExpiredSignature
return
end
#current_user ||= User.find_by(auth_token: auth_token)
end
end
def log_out(user)
logged_in? ? user.generate_authentication_token! : user.destroy_token!
auth_token = user.auth_token
user.update_attribute(:auth_token, auth_token)
end
def logged_in?
current_user.present?
end
def authenticate_with_token!
render json: { errors: "Not authenticated" }, status: :unauthorized unless logged_in?
end
def log_in(user)
create_session(user)
user.generate_authentication_token!
user.update_attribute(:auth_token, user.auth_token)
end
def authenticate_as_self_or_admin!
render json: { errors: "Not authorized" }, status: :unauthorized unless is_self? || is_admin?
end
def is_self?
user = User.find(params[:id])
auth_token = request.headers["Authorization"]
auth_token = auth_token.split(" ").last if auth_token
user.auth_token != auth_token
end
def is_admin?
if logged_in? && current_user.authenticate(params[:password])
current_user.admin
end
end
end
sessions_controller.rb
class Api::SessionsController < ApplicationController
before_action :authenticate_with_token!, only: [:destroy]
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
render json: user, status: :created
else
render json: user, status: :unprocessable_entity
end
end
def destroy
log_out current_user
render status: 204
end
end
user.rb
require 'json_web_token'
class User < ApplicationRecord
attr_reader :current_password
before_save { email.downcase! }
before_create :generate_authentication_token!
before_update :reset_confirmed!, :if => :email_changed?
has_secure_password
has_many :posts
has_many :comments
has_many :votes
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :username, presence: true, length: { maximum: 24 }, uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 8 }
validates :auth_token, uniqueness: true
def generate_authentication_token!
begin
self.auth_token = JsonWebToken.encode('id' => self.id, 'username' => self.username, 'email' => self.email, 'bio' => self.bio, 'confirmed' => self.confirmed, 'admin' => self.admin, 'points' => self.points)
end while self.class.exists?(auth_token: auth_token)
end
def destroy_token!
self.auth_token = nil
end
def reset_confirmed!
self.confirmed = false
end
def upvotes
self.votes.where(polarity: 1)
end
def downvotes
self.votes.where(polarity: -1)
end
def update_with_password(user_params)
current_password = user_params.delete(:current_password)
user_params[:password] = current_password if user_params[:password].nil?
if self.authenticate(current_password)
self.update(user_params)
else
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
false
end
end
end
No, I am not using devise.
I'm really hoping my eyes are just tired here...
It turns out that current_user was in fact being called since it is the default scope_name for Active Model Serializers. I changed the name of my current_user method to avoid this conflict. Here are the relevant docs.

Having Log in & Session Errors

First I'd like to let you know I can successfully create a new user and start a session for that new user. What I cannot do is then log in as that new user.
Not to distract anyone from answering but I think the error is within Authenticating the User.
Currently if in the sessions controller. If I have the below.
user = User.find_by(params[:email])
if user && user.authenticate(params[:session][:password])
I will get redirected to the login page because the password doesn't match.
I can login if I have the below. But I will not start the session as the correct user.
user = User.find_by(params[:email])
if user
Again I can create new users, my user count does increase, but I cannot log in with the user once I end the session.
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(params[:email])
if user && user.authenticate(params[:session])
session[:user_id] = user.id
case user.admin_level
when 1
session[:site_admin] = true
session[:can_create_post] = true
when 2
session[:creator] = true
session[:can_create_post] = true
when 3
session[:friend] = true
session[:can_comment_on_post] = true
end
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token
else
cookies[:auth_token] = user.auth_token
end
log_in user
redirect_to user, :notice => "Logged in!"
else
flash.now[:danger] = 'Invalid email/password combination' # Not quite right!
redirect_to root_url
end
end
def loggedin?
#user = session[:user_id]
redirect_to page_path(current_user)
end
def destroy
log_out if logged_in?
cookies.delete(:auth_token)
session.clear
redirect_to root_url, :notice => "Logged out!"
end
def session_params
params.require(:session).permit(:user_id)
end
end
My User Model Looks like below
class User < ActiveRecord::Base
attr_accessor :remember_token, :image
before_create { generate_token(:auth_token) }
mount_uploader :image, ImageUploader
mount_uploader :avatar, AvatarUploader
validates :name, presence: true, length: { maximum: 50 }
before_save { email.downcase! }
before_save { name.capitalize! }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true , length: { maximum: 255 },
format: {with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
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 forget
update_attribute(:remember_digest, nil)
end
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
end

User model won't update

I try to update an existing user:
Controller-Snippet
def account_settings
#user = current_user
end
def set_account_info
old_user = current_user
# verify the current password by creating a new user record.
#user = User.authenticate_by_username(old_user.username, params[:user][:password])
# verify
if #user.nil?
#user = current_user
#user.errors[:password] = "Das eingegebene Passwort ist falsch."
render :action => "account_settings"
else
# update the user with any new username and email
#user.update(params[:user])
# Set the old email and username, which is validated only if it has changed.
#user.previous_email = old_user.email
#user.previous_username = old_user.username
if #user.valid?
# If there is a new_password value, then we need to update the password.
#user.password = #user.new_password unless #user.new_password.nil? || #user.new_password.empty?
#user.save
flash[:notice] = 'Benutzerkonto-Einstellungen wurden übernommen.'
redirect_to :root
else
flash[:error] = #user.username
render :action => "account_settings"
end
end
end
I allready tried post-, put- and patch-method.
Route-Snippet
Calendar::Application.routes.draw do
root "welcome#index"
get "user/account_settings" => "user#account_settings"
patch "user/account_settings" => "user#set_account_info"
end
User-Model
class User < ActiveRecord::Base
attr_accessible :email, :username, :previous_email, :previous_username, :password, :password_confirmation, :new_password, :new_password_confirmation
attr_accessor :password, :new_password, :previous_email, :previous_username
before_save :encrypt_password
validates_confirmation_of :password
validates_confirmation_of :new_password, :if => Proc.new {|user| !user.new_password.nil? && !user.new_password.empty? }
validates_presence_of :password, :on => :create
validates_presence_of :email, :if => Proc.new {|user| user.previous_email.nil? || user.email != user.previous_email}
validates_presence_of :username, :if => Proc.new {|user| user.previous_username.nil? || user.username != user.previous_username}
validates_uniqueness_of :email, :if => Proc.new {|user| user.previous_email.nil? || user.email != user.previous_email}
validates_uniqueness_of :username, :if => Proc.new {|user| user.previous_username.nil? || user.username != user.previous_username}
def initialize(attributes = {})
super # must allow the active record to initialize!
attributes.each do |name, value|
send("#{name}=", value)
end
end
def self.authenticate_by_email(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def self.authenticate_by_username(username, password)
user = find_by_username(username)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
The Flash-Notice flash[:error] = #user.username puts the new username, so I dont understand why the user isn't updated in the database.
First I thought my set_account_info-method isn't right, but I have no better idea how to check the inputs.Secondly I changed the form_for method from default(post) to put and then to patch, but that also didn't help.
With rails 4 attr_accessible is no longer used, instead we now have strong params. what this means is that you now need to tell the application what parameters it can update (A whitelist).
This is all done in the controller now, I will give you an example of what I do and see if it helps you in your scenario
class ExampleController < ApplicationController
def create
#object= Object.new(my_params)
if #object.save
redirect_to root_path, notice: 'Object Successfully Created'
else
render action: 'new'
end
end
def update
#object= Object.find(params[:id])
if #object.update_attributes(my_params)
redirect_to root_path, notice: 'Object updated'
else
render action: 'edit'
end
end
private
def my_params
params.require(:object).permit(:id, :title, :overview, :category_id,
nested_attributes: [:id, :gallery_id, :gallery_category_id, :photo, :_destroy])
end

Michael Hartl's Rails Tutorial: undefined method `authenticate_with_salt' error

I'm at the end of 9th chapter and i'm getting an error on the root page itself..
sessions helper is
module SessionsHelper
def sign_in(user)
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
current_user = user
end
def current_user
#current_user ||= user_from_remember_token
end
def signed_in?
!current_user.nil?
end
def sign_out
cookies.delete(:remember_token)
current_user = nil
end
private
def user_from_remember_token
User.authenticate_with_salt(*remember_token)
end
def remember_token
cookies.signed[:remember_token] || [nil, nil]
end
end
here is my user.rb
require 'digest'
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40 }
before_save :encrypt_password
def signed_in?
!current_user.nil?
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
def self.authenticate_with_salt(id, cookie_salt)
user = find_by_id(id)
(user && user.salt == cookie_salt)? user : nil
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
end
and my session_controller is
class SessionsController < ApplicationController
def new
#title = "Sign in"
end
def create
user = User.authenticate(params[:session][:email], params[:session][:password])
if user.nil?
flash.now[:error] = "Invalid email/password combination."
#title = "Sign in"
render 'new'
else
sign_in user
redirect_to user
end
end
def destroy
sign_out
redirect_to root_path
end
end
and users_controler is
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#title = #user.name
end
def new
#user = User.new
#title = "Sign up"
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
#title = "Sign up"
render 'new'
end
end
end
thing is i'm a beginer in Rails and i was going through this tutorials n got stuck right here.
Pls help guyz
here is the screenshot of the error
error says: undefined method 'authentication_with_salt'
I think it is similar issue that was asked and the link is here
You miss current_user scope. Check the above link and hope you can fix your issue asap.

Rails - How to - Allow login only if a boolean is present

I am new to Rails and I am trying to allow users to login ONLY if a user has a variable email_activation_token set to true. For some reason I created the if statement but it allows a user to sign in REGARDLESS IF IT IS VALUE! I went to sqlitebrowser and checked my database and the user I created indeed has email_activation_token set to false.
Be mindful of two things. I am using HAML and I have a cookie to keep user logged in.
(Bonus points for any answer that has a link to a tutorial on the topic as it would be helpful)
app/controllers/sessions_controller.rb
def create
user = User.authenticate(params[:email], params[:password])
if user.email_activation_token = true
if user
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token
else
cookies[:auth_token] = user.auth_token
end
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
else
flash.now.alert = "You account has not been activated yet check your email!"
render "new"
end
end
app/controller/users_controller
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
UserMailer.registration_confirmation(#user).deliver
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
def accept_invitation
#user = User.find_by_email_activation_token!(params[:token])
#user.email_activation_token = true
redirect_to root_url, :notice => "Email has been verified."
end
end
end
app/views/sessions/new.html.haml
%h1 Log in
= form_tag sessions_path do
%p
= label_tag :email
= text_field_tag :email, params[:email]
%p
= label_tag :password
= password_field_tag :password
%p.button
%input{name: "commit", type: "submit", value: "Log in"}
.field
= label_tag :remember_me
= check_box_tag :remember_me, 1, params[:remember_me]
%p
= link_to "forgotten password?", new_password_reset_path
app/models/user.rb
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
before_save { |user| user.email = email.downcase }
before_create { generate_token(:auth_token) }
# before_create { generate_token(:email_activation_token) }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
VALID_PASSWORD_REGEX = /^(?=.*[a-zA-Z])(?=.*[0-9]).{6,}$/
validates_confirmation_of :password
validates :password, :on => :create, presence: true, format: { with: VALID_PASSWORD_REGEX }
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
app/config/routes.rb
LootApp::Application.routes.draw do
get "password_resets/new"
get "sessions/new"
resources :users
resources :sessions
resources :password_resets
resources :email_activations
resources :users do
collection do
get :accept_invitation
end
end
# get "users/new"
get "static_pages/home"
get "static_pages/help"
root to: 'static_pages#home'
match "sign_up", to: "users#new"
match '/help', to: 'static_pages#help'
match '/log_in', to: 'sessions#new'
match '/log_out', to: 'sessions#destroy'
end
app/controllers/sessions_controller.rb:
def create
# ...
if user.email_activation_token = true
# ...
Should be:
def create
# ...
if user.email_activation_token == true
# ...

Resources