I'm working through the Rails Tutorial, by Michael Hartl, and a question popped up, as I was creating an admin user.
I followed the instructions, and created an admin_user, who has access to the :destroy method. It also isn't attr_accessible, so people can't simply put a put request via the browser and change themeselves to admin.
But, I have a two-part question--
1) How would I make a user admin?
I though I would need to write something like this in the console
rails console
user = User.find(params[:101])
user.toggle!(:admin)
When I try that, I get
Undefined Local Variable or Method 'Params' for main:Object
2) Assuming that it is possible to make myself an admin, what's stopping other people from making themselves admin using a command line as well?
Here's a copy of the users_controller, I think Michael addressed this in the tutorial, and I followed his instructions, but I don't get how the below code prevents someone from going to the command line and making themselves admin
class UsersController < ApplicationController
before_filter :signed_in_user,
only: [:edit, :update, :index, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
def destroy
User.find(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_url
end
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
end
def new
unless signed_in?
#user = User.new
else
redirect_to #current_user
end
end
def create
unless signed_in?
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
else
redirect_to #current_user
end
end
def edit
end
def update
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
I would really appreciate your help clearing things up!
User.find(params[:101]) is appropriate only for http browser requests. If you visit http://www.example.com?101=test, then you can use params[:101] with value "test". But in console you can't use params unless you declare it. In your case the wright way will be User.find(101), if 101 is user id.
Other people can't make them admin because you didn't add attr_accessible for admin field. How can they do it via command shell? They have no access to command line. If they are it's a serious security breach.
Related
I've been following Rails Tutorial from Michael Hartl, https://www.railstutorial.org/book/updating_and_deleting_users.
I am having an error to pass one of the action level tests for admin access control test.
The test failed is as follows:
def setup
#user = users(:jessie) #admin
#other_user = users(:brenda) #non_admin
end
test "should redirect destroy when logged in as a non-admin" do
log_in_as(#other_user)
assert_no_difference 'User.count' do
delete user_path(#user)
end
assert_redirected_to root_url
end
The error that the terminal gives me is :
FAIL["test_should_redirect_destroy_when_logged_in_as_a_non-admin", UsersControllerTest, 1.6799899999750778]
test_should_redirect_destroy_when_logged_in_as_a_non-admin#UsersControllerTest (1.68s)
Expected response to be a redirect to root_url but was a redirect to login_url.
Expected root_url to be === login_url.
test/controllers/users_controller_test.rb:65:in `block in '
My users_controller.rb file is:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def show
#user=User.find(params[:id])
end
def new
#user= User.new
end
def create
#user=User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #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(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def index
#users = User.paginate(page: params[:page])
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
#Confirms a logged_in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the current user.
def correct_user
#user = User.find(params[:id])
recirect_to(root_url) unless current_user?(#user)
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Confirms an admin user
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
As I've been following the tutorial strictly and I am still very new to ruby on rails, it's very hard for me to identify where went wrong. I've been googling for solutions for a few hours, but in vain. Any help would be greatly appreciated. Thank you :)
I am currently in the process of learning Ruby on Rails through Michael Hartl's Rails Tutorial (Chapter 12). I am suddenly getting the below error.
UsersControllerTest#test_should_redirect_destroy_when_not_logged_in:
NoMethodError: undefined method admin?' for nil:NilClass
app/controllers/users_controller.rb:92:inadmin_user'
test/controllers/users_controller_test.rb:48:in block (2 levels) in <class:UsersControllerTest>'
test/controllers/users_controller_test.rb:47:inblock in '
Here is my test code:
test "should redirect destroy when not logged in" do
assert_no_difference 'User.count' do
delete :destroy, id: #user
end
assert_redirected_to login_url
end
And the rest of my code:
class UsersController < ApplicationController
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
:following, :followers]
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
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 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 following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
Can someone please have a look and tell me where I am going wrong? The code has basically been copied from the tutorial so I'm at a loss.
Thanks.
I had this same error in the Hartl tutorial. The problem was a missed :destroy action in listing 9.53 in users_controller.rb. Tests were back to green once I found that missing bit.
That said, it would be great if someone could explain precisely why this is the case.
The correct code:
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
Change admin_user method like this:-
def admin_user
redirect_to(root_url) unless current_user.present? && current_user.admin?
end
This will check first that current_user is present or not then check second condition(current_user.admin?). If current_user.present? will false then it will not check second condition.
You need to update your admin_user method to check if user is even logged in.
# Confirms an admin user.
def admin_user
redirect_to(root_url) if current_user.nil? || !current_user.admin?
end
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.
My code for post controller is as follows. What I'm trying to do is user should be able to delete his own posts, and admin_user can delete any posts. The following code make admin_user can delete posts, but for normal user, when trying to delete his own post, it redirect to root_path
It seems do_authentication doesn't work properly, a normal user is trying to be authenticated as an admin instead of "correct_user"
What could be wrong?
Thanks!
class PostsController < ApplicationController
before_filter :signed_in_user, only: [:index, :create, :destroy]
before_filter :do_authentication, only: :destroy
.
.
.
def destroy
#post.destroy
flash[:success] = "Post deleted!"
redirect_back_or user_path(current_user)
end
private
def do_authentication
correct_user || admin_user
end
def correct_user
#post = current_user.posts.find_by_id(params[:id])
redirect_to user_path(current_user) if #post.nil?
end
def admin_user
#post = Post.find_by_id(params[:id])
redirect_to(root_path) unless current_user.admin?
end
First of all I would suggest using cancan for authorization.
I think your problem is the return value of correct_user. You don't control that. If that method returns something that evaluates to false the do_authentication method will also call admin_user. Also looking at your code it seems that the admin authorization won't work also ...
try this:
def do_authentication
#post = Post.find_by_id(params[:id])
redirect_to redirect_location unless current_user.admin? or #post.user == current_user
end
def redirect_location
return "redirect_location_for_admin" if current_user.admin?
return "redirect_location_for_non_admins"
end
The methods correct_user,admin_user will be executed for all the users regardless of their role because you have not checked any condition while calling the methods.The code need to be improved to solve your problem.
def do_authentication
if current_user.admin?
#post = Post.find_by_id(params[:id])
else
#post = current_user.posts.find_by_id(params[:id])
redirect_to user_path(current_user), :notice => "Access denied" unless #post
end
end
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