I am trying to create an authentication system on RAILS with find_by_user_name_and_password()
I have the following problem:
undefined method `find_by_user_name_and_password' for #<Class:0x007fae373d5698>
Rails.root: /ror/blog/Blog`
Application Trace | Framework Trace | Full Trace
app/controllers/sessions_controller.rb:7:in `create'
Here is my code from sessions_controller
class SessionsController < ApplicationController
skip_before_filter :authorizes
def new
end
def create
user = User.find_by_user_name_and_password(params[:name], params[:password])
if user
session[:user_id] = user.id
redirect_to admin_url
else
redirect_to login_url, alert: "Bad datas"
end
end
def destroy
session[:user_id] = nil
rederect_to blog_url, notice: "End seans"
end
end
model defenition:
class User < ActiveRecord::Base
attr_accessible :name, :password_digest, :password_confirmation
validates :name, presence: true, uniqueness: true
#validates_confirmation_of :password
has_secure_password
end
Migrate
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.string :password_digest
t.timestamps
end
end
end
Looking at your model that you just posted, I see you don't have a password field... so no find_by_whatever_and_password.
You need to use
user = User.find_by_name(params[:user][:name])
if user && user.authenticate(params[:user][:password])
# etc.
Related
I need write validation for role parameter of accounts_controller. If records with role admin only one, this record cannot update attribute role to user.
This validation must close security bug. But I don't know how it write.
accounts_controller.rb
class AccountsController < ApplicationController
def index
#accounts = Account.all
end
def update
#account = Account.find(params[:id])
redirect_to accounts_path if account.update(role: params[:role])
end
end
account.rb
class Account < ApplicationRecord
enum role: [:user, :admin]
end
schema.rb
create_table "accounts", force: :cascade do |t|
t.integer "role", default: 0
end
I trying write something like this: validate :role, less_than_or_equal_to: 1 but it didn't works.
account.rb
validate :the_last_admin
protected
def the_last_admin
if Account.admin.count < 2
errors.add(:role, 'You are the last admin!')
end
end
I am using this command in my views/welcome/index.html.erb:
<% if current_user.admin? %>
<%= link_to 'Post New Load Data!', new_article_path %>
<% else %>
<% end %>
Earlier today, this would allow only my admin user to see this path.
I installed Devise a few hours ago but ended up not liking it. So I have gone through and removed what I thought was every file that it created.
Earlier, if I wanted a regular user to see the path, I would use...
<% if current_user %>
instead of
<% if current_user && current_user.admin? %>
I don't if that is what is creating my problem. My migrations did get messed up so I had to reset everything and I created new migration under db/migrate/20161209013349_create_users.rb:
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.string :password_digest
t.string :admin, :boolean, null: false, default: false
t.timestamps
end
end
end
I checked my MySQL users table and the non admin user has a 0 under the admin column. My admin user has as 1 under the admin column. There is a 0 under the boolean column for both regular user and admin.
my application_controller.rb:
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
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
def authorize
redirect_to '/login' unless current_user
end
end
users_controller.rb:
class UsersController < ApplicationController
def new
end
def create
if User.exists?(email: params[:user][:email])
redirect_to '/articles?user_or_pass_already_exists'
else
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
Any ideas on what caused this?
I think it's the way I set up this model.
EDIT
I also created this method for my admin in /app/models/user.rb:
class User < ApplicationRecord
has_secure_password
# convienience method to access the vaulue of admin:
def admin?
admin
end
# this makes sure the same email and user can't be registered twice
# this only works well if you are only wanting to validate one field such as name
#validates :email, uniqueness: true
end
This is why I am using admin? in my current_user.admin?
First, You must understand that even If you hide links from user You need server side check (authorization) before every action. cancancan gem will help you.
Now, for your question,
def admin?
admin
end
This method will return value of admin column.
According to this line in your migration
t.string :admin, :boolean, null: false, default: false
My guess is column type of admin column in database is string. Where It should be of type boolean. All string values are true in ruby.
2.2.0 :004 > if "0"
2.2.0 :005?> puts "I am true"
2.2.0 :006?> end
(irb):6: warning: string literal in condition
I am true
So either change your column type to boolean or change your method in user.rb like this
def admin?
admin == "1"
end
I clearly made the role for users (as you can see down below) but it says it doesn't exist. Help please? By the way, you can see how I'm hardcoding myself.
app/controllers/application-controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def require_user
redirect_to '/login' unless current_user
end
def require_admin
redirect_to '/' unless current_user.admin
end
end
User.create(first_name: "Johnny", last_name: "Appleseed", email: "j.appleseed#example", password: "MY AWESOME PASSWORD THAT NOBODY KNOWS", role: "admin")
db/migrate/20160109170743_create_users:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.string :password_digest
t.string :role, :default => "reader"
t.timestamps null: false
end
end
end
app/controllers/users-controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :role)
end
end
Without knowing the specific error message, I can only speculate that your error is here:
def require_admin
redirect_to '/' unless current_user.admin
end
Regardless of the attributes you have in your model / db, you'll only get instance methods you've defined. You don't have admin in your User object, thus making current_user.admin invalid.
You'd need to use the following:
#app/models/user.rb
class User < ActiveRecord::Base
def admin?
self.role == "admin"
end
end
current_user.admin? #-> true / false
Whilst the question mark isn't required, it denotes the evaluation of an object's properties (true / false).
As an aside, you may want to look at adding an enum to your User model:
#app/models/user.rb
class User < ActiveRecord::Base
enum role: [:reader, :admin]
end
This will give you a series of instance & class methods to better help your logic:
#user = User.find params[:id]
if #user.admin?
...
#admins = User.admin
#-> collection of "admin" users
To do it, you'll need to change your role column from string to integer
I would suggest that you use boolean for the column role.
User.rb
def admin?
self.role == true
end
so you can do
redirect_to '/' unless current_user.admin?
I followed RailsCast 274 to add Remember Me & Reset Password functionality to my app.
I have no problem locally, the app seems to run and authenticate users fine. The problem is when I deploy the production version to Heroku I get the error:
undefined method `find_by_auth_token!' for #<Class:0x007f35fbe37a78>
current_user is defined in my ApplicationController as:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user
before_action :require_user
def current_user
#current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
end
def require_user
if current_user.nil?
redirect_to new_session_path
end
end
end
This is my SessionsController:
class SessionsController < ApplicationController
layout false
skip_before_action :require_user
def create
user = User.find_by(email: params["email"])
if user && user.authenticate(params["password"])
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token
else
cookies[:auth_token] = user.auth_token
end
redirect_to root_path, notice: "Login successful!"
else
redirect_to new_session_path, alert: "Email or password incorrect"
end
end
def destroy
cookies.delete(:auth_token)
redirect_to new_session_path, notice: "Logout successful!"
end
end
And this is the User model:
class User < ActiveRecord::Base
has_secure_password
has_one :patient, :dependent => :destroy
has_one :clinician, :dependent => :destroy
accepts_nested_attributes_for :patient, :allow_destroy => true
accepts_nested_attributes_for :clinician, :allow_destroy => true
validates :password,
:length => { :minimum => 6 }, :if => :password_digest_changed?
validates_presence_of :password, on: :create
before_validation(on: :update) do
# only want confirmation validation to run if user enters password
self.password_confirmation = nil unless self.password.present?
end
# validates_uniqueness_of :email
before_create { generate_token(:auth_token) }
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 generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
And in my schema.rb:
create_table "users", force: true do |t|
t.string "timezone"
t.boolean "terms_agreement", default: false
t.string "email"
t.string "password_digest"
t.string "auth_token"
t.string "password_reset_token"
t.datetime "password_reset_sent_at"
end
Why is this working in development but not production?
Ruby 2.2.1 & Rails 4.1.8
development:
PostgresSQL 9.4.1
It's an old tutorial, rails 4 has different dynamic matchers
Rails 3
User.find_by_auth_token!(cookies[:auth_token])
Rails 4
User.find_by!(auth_token: cookies[:auth_token])
Currently I'm using Rolify & CanCan to manage roles and abilities in my Rails 3 app. My question is: How can I get a user to have a role by default on creation? for example, if I have a "user" role, ¿How can I make all the users that register in my app have a user Role by default? My Ability.rb has this code:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :user
can :update, User, :id => user.id
end
end
end
My User Model has this one:
class User < ActiveRecord::Base
rolify
authenticates_with_sorcery!
attr_accessible :username, :email, :password, :password_confirmation
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :username
validates_uniqueness_of :username
validates_presence_of :email
validates_uniqueness_of :email
end
The Role Model This One:
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
end
And From the UsersController we have:
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to users_path, :notice => "Tu usuario se ha guardado"
else
render "new"
end
end
Finally the Rolify Migration is this one:
class RolifyCreateRoles < ActiveRecord::Migration
def change
create_table(:roles) do |t|
t.string :name
t.references :resource, :polymorphic => true
t.timestamps
end
create_table(:users_roles, :id => false) do |t|
t.references :user
t.references :role
end
add_index(:roles, :name)
add_index(:roles, [ :name, :resource_type, :resource_id ])
add_index(:users_roles, [ :user_id, :role_id ])
end
end
Now, I can assign roles manually from the rails console by using:
1 User.all
2 User.find(id)
3 User.add_role(:role)
But how can I assign automatically a default role when every user it's created?
Thanks!
You can use an active record callback to assign the role after the user is created. Something like
class User < ActiveRecord::Base
after_create :assign_default_role
def assign_default_role
add_role(:role)
end
end
Note that there's also an after_save callback but it's called EVERY time the user is saved. So if you edit the user and save it would try to add the role again. That's why I'm using the after_create callback instead.
You'd better check if a role is assigned before add_role. so I prefer:
class User < ActiveRecord::Base
after_create :assign_default_role
def assign_default_role
add_role(:normal) if self.roles.blank?
end
end
Forget it, Just had to add:
#user.add_role(:user)
in my create action right after the #user = User.new(params[:user]) line.
Figured it out by myself... I'm still learning :)
Thanks!
after_create :default_role
private
def default_role
self.roles << Role.find_by_name("user")
self.save
end