In my app, when a user logins he/she is redirected to the users profile page. Say he/she is redirected to http://localhost:3000/users/1
If he/she replaces 1 with any other number I want them to redirect to there
current profile no matter if users exits in the database or not
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
log_in user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
#current_user = nil
reset_session
redirect_to root_path
end
end
User Controller:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:new, :show, :edit, :update]
before_action :correct_user, only: [:new, :show, :edit, :update]
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(set_params)
if #user.save
redirect_to new_sessions_path
else
render 'new'
end
end
def show
#user = User.find(params[:id])
#posts = #user.posts
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(update_params)
redirect_to #user
else
render 'edit'
end
end
private
def set_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def update_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
end
Currenty if user type in search bar localhost:3000/users/5 and user with id 5 does not exists in database it shows error
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User with 'id'=3
but I want to simply redirect to currently logged in users profile page.
If users type in search bar localhost:3000/users/3 and user with this id exists in db , currenty it show an error that firefox is not able to process this request but i want it redirect to its default page i.e,,user's profile page.
Create another controller call it UserController and don't depend on id. Instead figure out the current user from the session and display that user. So the show method for this controller would look like this:
def show
#user = User.find(session["user_id]")
#posts = #user.posts
end
Also, you might want to protect your UsersController by validating if the current user has access to view / update the user being queried for.
Just change your UsersController#correct_user to catch ActiveRecord NotFound exception:
class UsersController < ApplicationController
...
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
rescue ActiveRecord::RecordNotFound
redirect_to(root_url)
end
end
I would use "where" and ".take" in Users show method. The find method brakes the code when it does not find anything
def show
#user = User.where("id" => params[:id]).take
if #user.present?
#posts = #user.posts
else
redirect_to(root_url)
end
end
Or you can redirect instead of root_url to a more friendly error view that shows User not found
Related
I got this error today when I tried to use some helper methods for the users controller:
AbstractController::DoubleRenderError (Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and
at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need
to do something like "redirect_to(...) and return".)
I put this following helpers in application_controller.rb :
class ApplicationController < ActionController::Base
def current_user
User.find_by :id=>session[:user_id]
end
def log_in?
!!session[:user_id]
end
def log_in_first
if !log_in?
session[:error]="You have to log in first to continue your operation"
redirect_to("/login") and return
end
end
def correct_user?
if !(current_user.id.to_s==params[:id])
session[:error]="You have no right to do this operation."
redirect_to "/"
return
end
end
end
and here is the user_controller.rb:
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 user_path(#user)
else
render 'new'
end
end
def show
log_in_first
#user = User.find_by id: params[:id]
correct_user?
if #user
render 'show'
else
redirect_to '/login'
end
end
private
def user_params
params.require(:user).permit(:name,:password,:email,:email_confirmation)
end
end
As you can see I tried to use both return and and return in log_in_first and correct_user?to fix the problem but it still doesn't work. Does anyone have any ideas?
The problem is in the show action, log_in_first redirects then the show action does whatever it wants, which is redirect or render. This is causing the error.
A better solution is to use before_action for your authentication and authorization and just let the user controller actions do their thing. Something like the below.
class ApplicationController < ActionController::Base
def current_user
User.find_by :id=>session[:user_id]
end
def log_in?
!!session[:user_id]
end
def authenticate_user!
if !log_in?
session[:error]="You have to log in first to continue your operation"
redirect_to("/login")
end
end
def authorize_user!
unless current_user&.id.to_s==params[:id]
session[:error]="You have no right to do this operation."
redirect_to "/"
end
end
end
class UsersController < ApplicationController
before_action :authenticate_user!, only: [:show]
before_action :authorize_user!, only: [:show]
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id]=#user.id
redirect_to user_path(#user)
else
render 'new'
end
end
def show
#user = User.find_by id: params[:id]
render 'show'
end
private
def user_params
params.require(:user).permit(:name,:password,:email,:email_confirmation)
end
end
i have this routes:
Rails.application.routes.draw do
root 'login#new'
get '/home/inicio', to: 'home#index'
scope '/login' do
get '/acesso', to:'login#new'
post '/acessorecebendo', to:'login#create', as:'user'
get '/sair', to:'login#destroy'
end
resources :login
resources :home
resources :produtos
resources :fornecedors
end
the Login controller:
class LoginController < ApplicationController
protect_from_forgery
def new
if session[:user]
#user = User.find(session[:user])
end
end
def destroy
reset_session
redirect_to "/login/acesso", notice: "VocĂȘ foi deslogado"
end
def create
user = User.validate(login_params[:email], login_params[:senha])
if user
session[:user] = user.id
redirect_to "/home/inicio", notice: "login feito com sucesso"
else
redirect_to "/login/acesso", notice: "Dados incorretos"
end
end
private
def login_params
params.require(:login).permit(:email, :senha)
end
end
The home controller:
class HomeController < ApplicationController protect_from_forgery with: :exception
def new
#user = User.find_by(id: session[:user]) end
def index
#produtos = Produto.all
render 'inicio' end
def show
if session[:user]
#user = User.find(session[:user])
end end end
I'm getting an error on the Home view (new.html.erb):
<header>
<h2>Bem-vindo <%= #user.nome %></h2>
<nav>
undefined method `nome' for nil:NilClass
Why i have some problems with the session? I can do the login and i wanna see the user informations of this session on the redirected page, like if i can pass the #user variable assigned on the login action to the home controller to use it.
This is happening because your #user is being set only in index and show, but you are trying to reference it from the new action.
Consider moving this logic to a before_action
class HomeController < ApplicationController
before_action if: ->{ session[:user] } do
#user = User.find_by(id: session[:user])
end
end
If this controller needs to assume that #user is present, you should also have a before_action that handles the case of a missing user account. I usually put this behavior into a AuthenticatedController class and inherit from it where needed.
User.find is not optimal here, because it will throw an exception if no record is found.
Hello guys so what i want to do is simple.
I want a before filter to check if the current user is admin, to automatically redirect him to the admin panel even if the admin accesses the index page(e.g localhost:3000 which is the root url for normal users)
for the admin users the path is localhost:3000/admin/
I have written these blocks of code so far but cause a redirect loop
in my application_controller.rb i have this
include SessionsHelper
before_filter :admin_users?
private
def admin_users?
if current_user.admin?
redirect_to admin_path
end
end
and the current_user method is the following which is in sessionshelper.rb
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?(:remember, cookies[:remember_token])
log_in user
#current_user = user
end
end
end
here is users controller
class UsersController < ApplicationController
before_filter :admin_users? # application controller method
before_action :logged_in_user, only: [:index,:edit,:update] # is in Application Controller
before_action :correct_user, only: [:edit,:update]
before_action :admin_user, only: :destroy
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def edit
#user = User.find(params[:id])
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:name,:email,:password,:password_confirmation)
end
# before filters
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
any clues?
The issue is next. You have put your redirect inside application_controller.rb, that all other controller inherit from, even Admin controller.
You have to do next:
remove before_filter :admin_user from your ApplicationController, and put it only where you want to check if user is admin, all controllers except admin controller.
You have redirect loop because admin controller will redirect to admin_path, when you come to admin_path.
i have a rails app with sorcery
everything work .
the problem is when edit a user like :
http://localhost:3000/users/1/edit
its work fine , but when i change the user id to 2 or 3 ..
i can update all users data
how can i restrict the edit page only if the current user is the one that logged in
here is my controller :
skip_before_action :require_login, only: [:new, :create, :show]
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
auto_login(#user)
flash[:info] = "Welcome."
redirect_to root_url
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
you can also do something like this
before_action :edit_rights?, only: [:update, :edit]
private
def edit_rights?
#user = User.find(params[:id])
redirect_to(root_path) unless current_user == #user
end
you won't need #user = User.find(params[:id]) in both update and edit actions then
There are (at least) two ways to do that. First and straightforward is detailed in another answer, fine-tune your controller.
A less obvious way is to create a singular resource and its own controller. In routes that could look like:
resource :profile, only: [:show, :edit, :update]
# generates:
# /profile (GET, PATCH, PUT)
# /profile/edit (GET)
Then create a controller that is responible solely for user's own profile and operates only on current_user.
Yes, it's okay for one model to have multiple controllers, if your model should behave really differently in different parts of your app.
Why would you do that?
User's own profile could show much more information than is available publicly, you can lay it out in a separate view
No "access denied" errors, as the resource is auto-selected via current_user, all you need is ensure the user is logged in in the entire controller.
I have a simple user model with an edit page. Currently you can change your email and your password (with a password confirmation) but I don't currently require you to type your password again before changing any of that information.
I have a before filter that requires you to be logged in as well as a before filter to ensure you can only edit your own profile. However, in the case of public computers, I would like to re-authenticate a user by making them type their password.
I am using sorcery to back my authentication. How would I go about doing this. I don't see any methods for checking the password after being logged in. Below is my current users_controller
class UsersController < ApplicationController
before_filter :require_login, :only => [ :edit, :update ]
before_filter :correct_user, :only => [ :edit, :update ]
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to root_url, :notice => "Signed up!"
login_user(#user)
else
render :new
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated."
redirect_to root_url
else
render 'edit'
end
end
private
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user == #user
end
end
I've opened an issue on Sorcery's github page. It sounds like the creator will be opening a method called validate_credentials() in the future.
Source: https://github.com/NoamB/sorcery/issues/34#issuecomment-2108845