Denying unauthorized users from editing a profile - ruby-on-rails

I am trying to setup so that users will get a "not authorized" message if they click edit for a profile that is not theirs. This message should of course not appear for admins since admins can edit all profiles. I previously done this on Permission.rb, however I got rid of the file to go with a more basic user roles/authorization.
I don't see how I can implement what I had previously on Permission.rb for my current files. I have tried some solutions but they don't add up. If someone could point me in the right direction that will be great. Also I am doing this all from scratch, user authentication/authorization.
index.html.erb:
<% #users.each do |user| %>
<li>
<% if current_user.admin? || current_user == #user %>
<% end %>
<%= link_to "Edit #{user} profile", user %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?"} %>
</li>
<% end %>

Why are you giving the chance for users to edit other peoples profiles?
First, you should have a if statement in your view where you show the link for the edit page. I guess this is showing up on a profile of every user, so i suppose the code in your controller is something like this:
def show
#user = User.find(params[:id])
end
Then in your view you should have something like this:
<% if current_user.admin? || current_user == #user %>
<%= link_to 'Edit Profile' , edit_user_path(#user) %>
<% end %>
There is also a case if someone tries to 'force' their way in, just like trying to type a url www.yourapplication.com/users/6/edit you could write a before_filter method in your controller:
before_filter :check_privileges, only => [:edit, :update]
and then write a method in called check_privileges
def check_privileges
unless current_user.admin? || current_user.id == params[:id]
flash[:warning] = 'not authorized!'
redirect_to root_path
end
end
EDIT: After the questioner edited his code, i'm showing the mistake:
You are putting the end too soon:
<% #users.each do |user| %>
<li>
<%= link_to user.name, user %>
<% if current_user.admin? || current_user == #user %>
<%= link_to "Edit #{user} profile", user %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?"} %>
<% end %>
</li>
<% end %>

Related

Acts as Votable errors after user logs out

I have implemented act as votable, everything works well as long as a user is logged in, once a user logs out, I get the following error.
undefined method `voted_up_on?' for nil:NilClass
My index.html.erb
<p class="small-text float center ">
<% if current_user.voted_up_on?(startup) %>
<%= link_to '<i class="material-icons md-light">change_history</i>
</br>'.html_safe, downvote_startup_path(startup), method: :put %>
<small>votes: <%= startup.get_upvotes.size %></small>
<% current_user && current_user.voted_down_on?(startup) %>
<%= link_to '<i class="material-icons md-dark">change_history</i>
</br> '.html_safe, upvote_startup_path(startup), method: :put %>
<small>votes: <%= startup.get_upvotes.size %></small>
<% end %>
</p>
My Controller
before_action :find_startup, only: [:show, :edit, :destroy, :update, :upvote, :downvote]
def upvote
#startup.upvote_from current_user
redirect_to #startup, notice: "Upvoted successfully!"
end
def downvote
#startup.downvote_from current_user
redirect_to #startup, notice: "downvoted successfully!"
end
My routes
resources :startups do
member do
put :upvote
put :downvote
end
resources :comments, only: [:create, :destroy]
end
What am I missing?
In your index.html.erb you have this line of code <% if current_user.voted_up_on?(startup) %>, when you logout current_user method will be nil. Ensure that the current_user value is not nil before rendering the logic in your index.html.erb
something like
<% if current_user %>
...
# your logic that makes use of current_user object goes here
...
<% end %>
The issue is that after the user logs out you have no current_user. So the easiest solution to this would be to verify there is a current user first.
<p class="small-text float center ">
<% if current_user && current_user.voted_up_on?(startup) %>
<%= link_to '<i class="material-icons md-light">change_history</i>
</br>'.html_safe, downvote_startup_path(startup), method: :put %>
<small>votes: <%= startup.get_upvotes.size %></small>
<% end %> <-- couldn't find the closing end so maybe it goes here?
<% if current_user && current_user.voted_down_on?(startup) %>
<%= link_to '<i class="material-icons md-dark">change_history</i>
</br> '.html_safe, upvote_startup_path(startup), method: :put %>
<small>votes: <%= startup.get_upvotes.size %></small>
<% end %>
</p>
Your code was a little confusing, I think that second part was also an if conditional but maybe you forgot to write the if? Either way the fix is the check for current user in that first if conditional because a user can also access this page without being logged in, so you need to check for that, otherwise you are calling voted_up_on? on a nil object.

How to use boolean conditions

So I'm obviously confused with boolean flow because I run into a problem each time. NOTE: I am teaching myself to program, you are all my only hope in learning! I am trying to define a method that checks if a user is an admin, so that I can display certain objects in views to ONLY admins simple enough...or not, for some reason it's not recognizing that I'm an admin (when I truly am, I've checked!). With this all being said, I am a newb so go easy on me!
helpers/sessions_helper is used by both my User and Blogpost models:
def current_user #determines if user is logged out or is the current user of the session/cookie of profile
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 current_user?(user)
user == current_user
end
def is_an_admin
if current_user && current_user.admin?
end
end
<div class="col-xs-12">
<h1><%= #blogpost.title %></h1>
<p><%= #blogpost.content %></p>
<% if is_an_admin %>
<%= link_to "Return to blog", blogposts_path %>
<%= link_to "Edit Post", edit_blogpost_path, class: 'btn btn-primary' %> |
<%= link_to "Delete Post", blogpost_path(#blogpost),
method: :delete, data: {confirm: "Are you sure?"}, class: 'btn btn-danger' %>
<% else %>
<%= link_to "Return to blog", blogposts_path %>
<% end %>
</div>
I'm unsure if maybe I have the method in the wrong place? I have tried placing it inside my applications controller and my user controller to no avail. I must be missing something.
Your sintax got messed up, but I think you're always returning nil on the is_an_admin method:
def is_an_admin
if current_user && current_user.admin?
end
end
It does nothing if the condition is true so it's always returning nil
Change it to something like:
def is_an_admin?
current_user and current_user.admin? # or shorter: current_user.try('admin?')
end

Creating a fallback if current_user is nil for Devise

I have instances in my app that perform actions based on the current_user courtesy of Devise. For instance:
Controller
class PostsController < ApplicationController
def like
#post.liked_by current_user
end
end
View
...
<% if current_user.liked? post %>
<%= link_to "Unlike", unlike_post_path(current_user.to_param, post), method: :put, remote: true %>
<% else %>
<%= link_to "Like", like_post_path(current_user.to_param, post), method: :put, remote: true %>
<% end %>
My problem, cross-site is if there is no current_user then I'll get a NilClass error. I'm aware of the callback before_filter :authenticate_user! but was wondering what would be the best solution where the actions are still visible in the view for both logged in/non-logged in users.
Devise has a helper for this:
<% if user_signed_in? && current_user.liked?(post) %>
...
<% else %>
...
<% end %>
If they aren't signed in, the second part of the condition will never be evaluated so no Nil error.
You can also try the helper current_user like so:
<% if !current_user.nil? && current_user.liked?(post) %>
...
<% else %>
...
<% end %>
We are basically telling rails that if the current user is NOT nil and the current user has liked the post then we should see the unlike button, else we see the like button.

How to I get the destroy action to work with administrative control in Rails?

I am going through Michael Hartl's book, "Ruby on Rails Tutorial Learn Web Development with Rails". In the section where he explains how to get administrative access to delete users, I can't seem to get this to work. The delete link won't show up on the web app, and I am mystified as to why this is occurring. This is my unfactored code below, which sits in the directory of: app/views/users/index.html.erb
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<% #users.each do |user| %>
<li>
<%= gravatar_for user, size: 52 %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "Are you sure?" } %>
<% end %>
</li>
<% end %>
</ul>
<%= will_paginate %>
The methods of current_user.admin? && !current_user?(user) are methods located in: app/helpers/sessions_helper.rb
def current_user
remember_token = User.encrypt(cookies[:remember_token])
#current_user ||= User.find_by(remember_token: remember_token)
end
def current_user?(user)
user = current_user
end
I've been stuck on this problem for quite a while and have tried many different things to solve the problem, and none of them have worked. I'm running Rails on Windows 7 using the Rubymine IDE. The tutorial was done on a Mac OS X operating system, and I'm thinking maybe the problem might be associated with my setup.
Also are there known issues with tests failing using the Windows cmd when running RSpec versus running it on Mac OS X?
Any help would be much appreciated.
Thank you.
I just figured out what was wrong!
This method was written incorrectly
def current_user?(user)
user = current_user
end
The correct method to get the corresponding code above to work is this:
def current_user?(user)
user == current_user
end
Thank you for all your input!
Make sure in your controller you're calling the destroy action in your controller. Your code looks fine thus far and I'm assuming you're not using cancan for authorization.
In view file
<% if current_user.admin? && !current_user?(user) %>
<%= link_to "delete", user, method: :delete, data: { confirm: "Are you sure?" } %>
<% end %>
Controller
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_path }
format.json { head :no_content }
end
end

Making a user an admin user through a button without having the attribute accessible in Rails

From what I understand it would create a security flaw if I were to let my admin boolean in my users model be listed in attr_accessible. However I want a link on my show page that lets existing admin users grant admin privileges to other users. I was wondering how to go about doing this? My code in my show view for a user at the moment includes:
<% if current_user.admin? && #user.admin == false %>
<%= link_to "Make Administrator", '#',
data: { confirm: "Make this user an admin?" }, class: "btn btn-large btn-primary" %>
<% elsif current_user.admin? && #user.admin%>
<%= link_to "Remove Administrator", '#', class: "btn btn-large btn-danger" %>
<% end %>
I'm not entirely sure what to put instead of the '#'? #user.toggle!(:admin) doesn't seem to work so any pointers would be appreciated. Thank you in advance!
First, since this is an action this should be a button, not an link.
<%= button_to user_path(#user), :method => :put ... %>
controller code
def update
if params[:admin]
user.update_attribute(:admin, true)
redirect_to ...
end
...
end
or if you prefer to keep your logic in the model
def update
...
if params[:admin]
#user.make_admin
end
...
end
model code
def make_admin
self.update_column(:admin, true)
end

Resources