I have added a before filter and def check priv to the users controller. It is suppose to be setup so that only admin can view/edit all profiles, and that only the created User can view their own profile. As before anyone could view/edit profiles. I have tried a few methods, none work. When I go to view profile as admin or even regular user I get the "not authorized" message.
Any help would be appreciated.
users_controller:
before_filter :check_privileges, :only => [:edit, :update]
def check_privileges
unless current_user.admin? || current_user.id == params[:id]
flash[:warning] = 'not authorized!'
redirect_to root_path
end
end
index.html:
<%= 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 %>
I have a similar method in my app, try something like this:
def check_privileges
return if current_user.admin? # this user is an admin, if is not the case do:
flash[:warning] = 'not authorized!'
redirect_to root_path
end
UPDATE 1
Again, try to change the if condition as the follow
if (condition || condition)
or
if ((condition) || (condition))
The problem is that Ruby parsers stop at the first condition if not explicited declared.
UPDATE 2
I think that there are an error in the parentheses on your index.html.erb, try the following:
<%= 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 %>
Food for thought maybe you could do something like this:
def correct_user
#user = User.find(params[:id])
if current_user.role? :administrator
#Allow admin to access everyone account
else
access_denied unless current_user?(#user)
end
end
Then inside your view your view do the if statement. Or alternatively my best suggestion is to go with something like CanCan. Something like this will allow you to set up role authentication really easily. If you have a look at it you can set a certain amount of rule in your ability.rb which you can then enforce on the view.
If you WERE to go with the method of CanCan you could do something like this in your ability.rb
def initialize(user)
user ||= User.new # guest user
# raise user.role?(:administrator).inspect
if user.role? :administrator
can :manage, :all
can :manage, User
elsif user.role? :employee
can :read, :all
end
The above is an example.... So that in your views you can enforce this type of rule by doing something like
<%= link_to user.name, user %>
<% if can? :manage, #user %>
<%= link_to "Edit #{user} profile", user %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?"} %>
<% end %>
Something like this should work. Your options are there hope this helps :)
Related
I'm a little new to it, but I'm building a new web app using rails. Most of what I've got so far is based on railstutorial.org. I've only got a few possible user "roles" (basic user, excom, and admin), so I'm just modeling it using a couple boolean fields in the user model.
I'd like my admin users to be able to make other users admin or excom, without having to resort to some full blown user role modeling system.
I don't want admins to be able to modify other user data (like name, email, etc.) or of course allow users to make themselves admin, so adding something like that to the users_controller update method seems cumbersome and error prone. But it also seems like a whole new controller and routes is overkill.
I just want a button for admins to click to "Make user admin" and have it work, but I'm not sure of the "right" way to implement that.
Edit:
The only exposure an admin has at this point, is checking whether a user is an admin in some before_action. I.e.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
or
def correct_user_or_excom_or_admin
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user) || current_user.admin? || current_user.excom?
end
I think what I want is how to define a route such that I can write the following method in the users_controller and include it in the admin_user before_action.
def make_admin
#user = User.find(params[:id])
#user.admin = true
#user.save
flash[:success] = "#{#user.name} is now an Admin"
end
And then be able to include the following in the appropriate view
<%= link_to "Make Admin", user_admin_path(user), method: :post,
data: { confirm: "You sure?" } %>
I think #widjajayd answer is on the right track. Does creating custom routes that way include the user id in the params?
you can create custom route with custom method for admin
inside routes.rb, create 2 routes for new and create just for admin
resources users do
collection {
get :new_admin
put :create_admin
}
end
inside user_controllers.rb, create 2 methods
def new_admin
#user = User.new
# this depending with what system you use devise/bcryt/others
end
def create_admin
#user = User.new(user_params)
#user.role = "Admin"
# this depending with what system you use devise/bcryt/others
end
create view file inside app/users/new_admin.html.erb
<%= form_for #user, url: create_admin_users_path, do |f| %>
# your fields name, password, etc
<% end %>
button availability just for admin user
<% if user.role == admin %>
<%= link_to 'Make User Admin', new_admin_users_path, :class => 'form-control btn btn-info' %>
<% end %>
Edit with additional code if you want to make some user to be an admin
below usually you list user in index.html.erb
<% if #users.any? %>
<table id="table-user" class="table table-striped">
<thead>
<tr>
<th>email</th>
<th>name</th>
<th>Role</th>
<th class="edit"></th>
<th class="destroy"></th>
</tr>
</thead>
<tbody>
<tr>
<% #user.each do |user| %>
<td><%= user.email %></td>
<td><%= user.username %></td>
<td><%= user.role %></td>
<td><%= link_to "Make Admin", create_admin_users_path(user_id: user.id), method: :post,
data: { confirm: "You sure?" } %> </td>
<% end %>
</tbody>
</table>
<% end %>
from form you pass params with hash user_id (it can be any name you want) then inside create controller you get the params with sample below
def create_admin
#user = User.find(params[:user_id])
#user.admin = true
#user.save
flash[:success] = "#{#user.name} is now an Admin"
end
Here's the solution I came up with, with inspiration taken from #widjalayd.
Create the following custom routes.
post '/users/:id/make_admin', to: 'users#make_admin', as: :make_admin
delete '/users/:id/remove_admin', to: 'users#remove_admin', as: :remove_admin
post '/users/:id/make_excom', to: 'users#make_excom', as: :make_excom
delete '/users/:id/remove_excom', to: 'users#remove_excom', as: :remove_excom
Create the corresponding methods in the users_controller being sure they are in the admin_user before_action
def make_admin
#user = User.find(params[:id])
#user.admin = true
#user.save
flash[:success] = "#{#user.name} is now an Admin"
redirect_to users_url
end
def remove_admin
#user = User.find(params[:id])
#user.admin = false
#user.save
flash[:success] = "#{#user.name} is no longer an Admin"
redirect_to users_url
end
def make_excom
#user = User.find(params[:id])
#user.excom = true
#user.save
flash[:success] = "#{#user.name} is now an Executive Committee Member"
redirect_to users_url
end
def remove_excom
#user = User.find(params[:id])
#user.excom = false
#user.save
flash[:success] = "#{#user.name} is no longer an Executive Committee Member"
redirect_to users_url
end
And the partial for displaying a user on the index page is then
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
|
<%= link_to "Delete", user, method: :delete,
data: { confirm: "You sure?" } %>
|
<% if user.admin? %>
<%= link_to "Remove Admin", remove_admin_path(user), method: :delete,
data: { confirm: "You sure?" } %>
<% else %>
<%= link_to "Make Admin", make_admin_path(user), method: :post,
data: { confirm: "You sure?" } %>
<% end %>
|
<% if user.excom? %>
<%= link_to "Remove Excom", remove_excom_path(user), method: :delete,
data: { confirm: "You sure?" } %>
<% else %>
<%= link_to "Make Excom", make_excom_path(user), method: :post,
data: { confirm: "You sure?" } %>
<% end %>
<% end %>
</li>
And then write some tests to be sure.
test "admins should be able to make and remove new admins" do
log_in_as(#user)
post make_admin_path(#other_user)
assert #other_user.reload.admin?
delete remove_admin_path(#other_user)
assert_not #other_user.reload.admin?
end
test "non admins can't make or remove admins" do
log_in_as(#other_user)
delete remove_admin_path(#user)
assert #user.reload.admin?
post make_admin_path(#another_user)
assert_not #another_user.reload.admin?
end
test "admins should be able to make and remove executive committee" do
log_in_as(#user)
post make_excom_path(#another_user)
assert #another_user.reload.excom?
delete remove_excom_path(#another_user)
assert_not #another_user.reload.excom?
end
test "non admins can't make or remove executive committee" do
log_in_as(#another_user)
post make_excom_path(#user)
assert_not #user.reload.excom?
delete remove_excom_path(#other_user)
assert #other_user.reload.excom?
end
Edit:
This probably is pushing the limit of "good/maintainable" code and the "rails-way", which is why I asked the question. But since this works, and took a lot less time than learning and setting up a full blown roles system like devise I'll stick with it for now. If I need to make any significant changes then I will probably switch to devise.
For some reason, for a non logged in user, this code displays the html but once delete is clicked CanCan does not allow the action.
<% if can? :destroy, #boat %>
<%= link_to "", boat, method: :delete, data: { confirm: "You sure?" } %>
<% end %>
How do I prevent the HTML from displaying???
def initialize(user)
user ||= User.new
if user.admin? || user.email = 'test#test.io'
can :manage, :all
elsif user.manager?
can :read, Boat
can [:create, :read, :update], User
else
can :read, Boat
can :create, User
end
end
Some how the app was still detecting my email, even while logged out and therefore giving me admin privileges!
Not sure how this is happening...
I just set up Devise with Cancan for user roles. I think I'm on the right track, but I just ran into a situation where I think I'm missing something small.
I want any user with role :admin to be able to edit the profiles/roles of every other user. I have the routes set up right, but when I click on the links for other users, I get redirected.
_user.html.erb
<% #users.each do |user| %>
<li>
<%= gravatar_for user, size: 52 %>
<%= link_to user.name, user %>
<% if can? :assign_roles, #user %>
| <%= link_to "delete", user, method: :delete, confirm: "Delete user?" %>
| <%= link_to "edit", edit_user_path(user) %>
<% end %>
</li>
<% end %>
users_controller.rb
...
def edit
#user = User.find(params[:id])
end
def update
authorize! :assign_roles, #user if params[:user][:assign_roles]
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
ability.rb
def initialize(user)
can :assign_roles, User if user.admin?
can :manage, :all if user.is? :admin
end
I've been changing this code around all day, I might even be going in circles.
Any help would be greatly appreciated.
I figured it out. Even though I was able to limit the html/css with the logic shown about, I was not able to limit model/controller interaction. I have multiple controllers, and the one I was dealing with had no authentication check. So in other words, I added
before_filter :authenticate_user!
to my users_controller.rb file, and now it knows that I'm an admin, and what that means. I just added this on a whim, but everything I've learned about Devise/Cancan so far is from the wiki:
https://github.com/ryanb/cancan/#wiki-readme
First of all I am new to rails. I used devise gem for user authentication. Now I want to provide admin a way to delete other users. id of the user is not passing to my destroy action. Here is my code
user_controller.rb
class UsersController < ApplicationController
def destroy
User.find(params[:id]).destroy
redirect_to dashboard_path
end
end
dashboard.html.erb
<% if current_user.admin == true %>
<% #users = User.all %>
<% #users.each do |user| %>
<%= user.email %>
| <%= link_to "delete", destroy_path, method: :delete, data: { confirm: "Are you sure"} %><br>
<% end %>
<% end %>
First of all, You shouldn't assign instance variables directly in your views. This is a Controller responsibility. So, in the above example, the right thing to do is something like this:
# users_controller.rb
class UsersController < ApplicationController
def dashboard
#users = User.all
end
def destroy
User.find(params[:id]).destroy
redirect_to dashboard_path
end
end
And your view should look something like this:
# dashboard.html.erb
<% if current_user.admin == true %>
<% #users.each do |user| %>
<%= user.email %>
| <%= link_to "delete", user, method: :delete, data: { confirm: "Are you sure"} %><br>
<% end %>
<% end %>
And last but not least =P make sure your routes.rb have something like this:
# routes.rb
delete "/users/:id" => "users#destroy", as: :user
Of course it's just an example based on your question, but it should work like a charm =P
This is my session's controller code
def create
user = User.authenticate(params[:login], params[:password])
if user
session[:user_id] = user.id
redirect_to_target_or_default root_url, :notice => "Logged in successfully."
else
flash.now[:alert] = "Invalid login or password."
render :action => 'new'
end
end
I need a div id="welcomebuttons" located in layouts/application.html.erb to display when the user is not in session (logged out) but disappear completely and remain hidden when the user is logged in. I tried adding javascript:hideDiv_welcomebuttons() to the if user but of course that didn't work.
Could anyone help?
in application layout
<% if session[:user_id].nil? %>
<div id="welcomebuttons">
</div>
<% end %>
I'm using block helper like this (just added them to your application_helper.rb and you're good to go):
# application_helper.rb
def not_logged_in(&block)
capture(&block) unless session[:user_id]
end
def logged_in(&block)
capture(&block) if session[:user_id]
end
#application.html.erb
<div>I'm visible for everyone</div>
<%= logged_in do %>
<div>I'm only visible if you are logged in</div>
<% end %>
<%= not_logged_in do %>
<div>I'm only visible unless you are logged in</div>
<% end %>
You define a current_user method in the application controller:
def current_user
# Look up the current user based on user_id in the session cookie:
#TIP: The ||= part ensures this helper doesn't hit the database every time a user hits a web page. It will look it up once, then cache it in the #current_user variable.
#This is called memoization and it helps make our app more efficient and scalable.
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
and then use it as a condition for an if block in your layout:
<% if current_user %>
<div> <%= "Logged in as #{current_user.email}" %> | <%= link_to 'Home', root_path %> | <%= link_to 'Log Out', logout_path, method: :delete %> </div>
<% else %>
<div> <%= link_to 'Home', root_path %> | <%= link_to 'Log In', login_path %> or <%= link_to 'Sign Up', new_user_path %> </div>
<% end %>