I need to add the skip validation methods to my sessions controller but i am unsure what to define omniauth as and basic login as. Ie they are both from class User but in sessions controller do I put
def create
user = User.from_omniauth(env["omniauth.auth"])
**user.from_omniauth.skip_name_validation = true**
etc etc
Then also want to add validate for the methods in the model as put in bold and add that to my sessions controller so both instances of each login are able to be bypassed.
My sessions controller
def create
user = User.from_omniauth(env["omniauth.auth"])
unless user.present?
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
# Log the user in and redirect to the user's show page.
else
# Create an error message.
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
else
log_in user
redirect_to user
end
end
My model.rb
class User < ApplicationRecord
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 },
**unless: :skip_name_validation**
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 },
**unless: :skip_email_validation**
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil:true,
**unless: :skip_password_validation**
**attr_accessor :skip_name_validation, :skip_email_validation, :skip_password_validation**
**attr_accessor :skip_User, :skip_self**
**validate :User, unless: :skip_User**
**validate :self, unless: :skip_self**
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
class User < ApplicationRecord
attr_accessor :password_confirmation, :skip_password_validation
validates :password, :presence => true ,unless: :skip_password_validation
validates_confirmation_of :password, :if => ->{ password.present? },unless: :skip_password_validation
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
def create
user = User.from_omniauth(env["omniauth.auth"])
#######try this #####
user.skip_password_validation = true
####or try this ####
user.from_omniauth.skip_name_validation = true
unless user.present?
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
# Log the user in and redirect to the user's show page.
else
# Create an error message.
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
else
log_in user
redirect_to user
end
end
Related
I warmly welcome. I recently started learning RoR. Currently processed course Michael Hartl. I got to the lesson 9 - cookies. Unfortunately, I received an error that I can not fix. Please help.
Error:
ERROR["test_login_with_valid_information_followed_by_logout", UserLoginTest, 1.443278428982012]
test_login_with_valid_information_followed_by_logout#UserLoginTest (1.44s)
NoMethodError: NoMethodError: undefined method `remember' for #<User:0x0000000a0c5698>
Did you mean? remember_token
app/helpers/sessions_helper.rb:8:in `remember'
app/controllers/sessions_controller.rb:10:in `create'
test/integration/user_login_test.rb:23:in `block in <class:UserLoginTest>'
My files:
User.rb
class User < ApplicationRecord
attr_accessor :remember_token
before_save { email.downcase! }
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},
format: { with: VALID_EMAIL_REGEX},
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: {minimum: 6}
class << self
# Returns the hash digest of the given string.
def digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def new_token
SecureRandom.urlsafe_base64
end
def remember
remember_token = new_token
update_attribute(:remember_digest, digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
end
end
Sessions_controller.rb
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
remember user
redirect_to user
else
flash.now[:danger] = "Invalid email/password combination"
render 'new'
end
end
def destroy
log_out
redirect_to root_path
end
end
Sessions_helper.rb
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
# Remembers a user in a persistent session.
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
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
def log_out
session.delete(:user_id)
#current_user = nil
end
end
**user_login_test.rb**
require 'test_helper'
class UserLoginTest < ActionDispatch::IntegrationTest
def setup
#user = users(:marcin)
end
test "user invalid login" do
get login_path
assert_template 'sessions/new'
post login_path, params: { session: { email: "", password: "" } }
assert_template 'sessions/new'
assert_not flash.empty?
assert flash.any?
get root_path
assert_not flash.any?
end
test "login with valid information followed by logout" do
get login_path
assert_template 'sessions/new'
post login_path, params: { 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_path
follow_redirect!
skip
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(#user), count: 0
end
end
The problem is that you have wrapped the remember method within class << self, making it a class method of User instead of an instance method.
Remove the class << self and just put self in front of the methods you want to be class methods. Or you can move remember so it's outside the class << self.
....
has_secure_password
validates :password, presence: true, length: {minimum: 6}
def remember
remember_token = new_token
update_attribute(:remember_digest, digest(remember_token))
end
...
This should solve your problem, Just make the method remember an instance method by moving it out of the class << self declaration.
# frozen_string_literal: true
class User < ApplicationRecord
attr_accessor :remember_token
before_save { email.downcase! }
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 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
def remember
remember_token = new_token
update_attribute(:remember_digest, digest(remember_token))
end
class << self
# Returns the hash digest of the given string.
def digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def new_token
SecureRandom.urlsafe_base64
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
end
end
Approach #1
Send email reminder to <%= email_field_tag :email, nil, placeholder: 'Enter Email', class: "send-email" %> # :email belongs to the users table
Approach #2
Send email reminder to <%= f.email_field :default_email, class: "send-email", placeholder: "Enter Email" %> # :default_email belongs to the challenges table. The point of this is to try something in the create process like current_user.email == #challenge.default_email
The user is then redirected to signup url
If a user signs up via Facebook a random email is generated so that he can get through the validation process: user.email = SecureRandom.hex + "#mailinator.com"
user.rb
def self.from_omniauth(auth)
# Sets 60 day auth token
oauth = Koala::Facebook::OAuth.new("125402372971231236229929", "ee917abf2e8f1c98274cdafssadffddffaebb1346f4")
new_access_info = oauth.exchange_access_token_info auth.credentials.token
new_access_token = new_access_info["access_token"]
new_access_expires_at = DateTime.now + new_access_info["expires"].to_i.seconds
where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
user.provider = auth.provider
user.image = auth.info.image
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = new_access_token # auth.credentials.token <- your old token. Not needed anymore.
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.password = (0...8).map { (65 + rand(26)).chr }.join
user.email = SecureRandom.hex + "#mailinator.com" unless user.email.present?
user.activated = true
user.save!
end
end
But how can we make the email the user inputted in the challenges/create.html.erb form seen above override the SecureRandom email?
users_controller
def create
#user = User.new(user_params)
if #user.save
action = session.delete(:challenge_action)
deadline = session.delete(:challenge_deadline)
committed = session.delete(:challenge_committed)
date_started = session.delete(:challenge_date_started)
order = session.delete(:challenge_order)
days_challenged = session.delete(:challenge_days_challenged)
why = session.delete(:challenge_why)
conceal = session.delete(:challenge_conceal)
#user.challenges.create(action: action, deadline: deadline, why: why, conceal: conceal, date_started: date_started, committed: committed, days_challenged: days_challenged)
end
redirect_to root_url
else
render 'new'
end
end
challenges_controller
before_action :update_user_email, if: proc {|c| c.current_user.present? && c.params[:email].present? }
def create
#challenge = Challenge.new(challenge_params)
if params[:step] == '2'
if current_user == nil
# If there is no user, store values to the session.
session[:challenge_action] = challenge_params[:action]
session[:challenge_committed] = challenge_params[:committed]
session[:challenge_deadline] = [params["challenge"]["deadline(3i)"], params["challenge"]["deadline(2i)"], params["challenge"]["deadline(1i)"]].join('/')
session[:challenge_date_started] = [params["challenge"]["date_started(3i)"], params["challenge"]["date_started(2i)"], params["challenge"]["date_started(1i)"]].join('/')
session[:challenge_order] = challenge_params[:order]
session[:challenge_days_challenged] = challenge_params[:days_challenged]
session[:challenge_why] = challenge_params[:why]
session[:challenge_conceal] = challenge_params[:conceal]
redirect_to signup_path
else
#challenge = current_user.challenges.build(challenge_params)
#challenge.save
redirect_to root_path
end
else
respond_modal_with #challenge
end
end
private
def update_user_email
email = params[:email]
current_user.update_attribute(:email, email)
end
sessions_controller
class SessionsController < ApplicationController
def new
end
def facebook
user = User.from_omniauth(env["omniauth.auth"])
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
redirect_to root_url
end
def create
assign_email(cookies[:challenges_email])
user = User.find_by(email: params[:session][:email].downcase)
assign_email(cookies[:challenges_email])
if user && user.authenticate(params[:session][:password])
assign_email(cookies[:challenges_email])
log_in user
params[:session][:remember_me] == 'nil' ? forget(user) : remember(user)
redirect_to root_url
else
flash.now[:info] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_url
end
end
We talked about setting it as a cookie in another question. If you're taking that approach, you could pass the cookie to a method. Something like what is discussed in this question:
before_create :set_user_email
def assign_email(email)
#email = email
end
def set_user_email
self.email = #email
end
then in the controller action where you have access to the cookie:
assign_email(cookies[:challenge_email])
#joseph had answered it pretty simple but i think you can just get email in the field by setting it in the controller like;
User.new(:email => "email you want to show in the field")
no need to pass nil user object pass a new object.
And in the view file it will shown in email field with
Send email reminder to <%= f.email_field :email, class: "send-email", placeholder: "Enter Email" %>
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
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?
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