I am implementing a User modeling for my app. That only current user can update and destroy his account I put in the user controller:
before_action :correct_user, only: [:edit, :update, :destroy]
But now I also want that the admin can do those actions (well he can actually do everything)
I proposed to create another function in the helper admin_user to allow him the access to all actions then call it in the user controller like:
before_action :admin_user
but it seems like he is ignoring it. Any mathematic function to solve this issue?
Thanks!
You could just define that correct_user always returns immediately if the current_user is an admin:
def correct_user
return if current_user.admin?
# put existing logic here
end
Related
Users can edit their profile at mydomain.com/users/3/edit. But they can also visit mydomain.com/users/7/edit (or any other ID).
It doesn't actually affect the data at all, since I'm just using current_user but I'd like to change the path to mydomain.com/profile/edit. Currently just using:
resources :users
How would I alter this? Is it as simple as just adding below line, or is there a cleaner way of doing it?
get 'profile/edit', to: 'users#edit'
Also, what about to block 3rd party users to edit ?
In users_controller.rb
before_action :correct_user, only: [:edit, :update, :destroy]
private
def correct_user
redirect_to(root_url) unless current_user == #user
end
I have a devise user ( that I call a Provider in my app) and I am trying to write a custom authentication method to prevent the Providers from deleting each other's posts (called Procedures in my app). Right now I have the correct_user method in my procedures controller.
def correct_user
#provider = #procedure.provider.find(params[:id])
redirect_to(provider_path(current_provider)) unless current_provider?(#provider)
end
I call it with the following before filter, also in my procedures controller:
before_filter :correct_user, :except => [:index, :show]
And I get the following error when trying to edit a procedure, even the provider's own procedure:
NoMethodError (undefined method `provider' for nil:NilClass)
app/controllers/procedures_controller.rb:8:in `correct_user'
Parameters: {"id"=>"523"}
From the looks of this error, the correct_user method is finding the procedure id instead of the provider id. How can I fix this? Thanks
Authentication is about making sure that the user is who he says is. Devise is an authorization library. The only access control it provides is that you can make actions off limits for unknown users.
Authorization is making rules about who gets to do what. Popular libraries include Pundit & CanCanCan.
Even without a lib you could write a simple authorization rule like this:
class Provider < ActiveRecord::Base
class NotAuthorized < StandardError; end
end
class ApplicationController < ActionController::Base
rescue_from Provider::NotAuthorized, with: :deny_access
private
def deny_access
render 'some_view', status: 403
end
end
class ProceduresController < ApplicationController
before_action :find_procedure, only: [:show, :edit, :update, :destroy]
before_action :authorize_resource!, except: [:new, :index, :show]
# DELETE /procedures/:id
def destroy
# This line never gets run if the user is not authorized.
#procedure.destroy
end
private
def find_procedure
#procedure = Procedure.find(params[:id])
end
def authorize_resource!
unless current_provider == #procedure.provider
raise Provider::NotAuthorized and return false
end
end
end
Notice that in the authorize_resource! method you compare the user id of the record that you are authorizing against the user id from the session.
If you used the id from the params you're leaving yourself wide open to a spoofing attack where a user pretends to be someone else by passing another user's id in the params.
However, I would not recommend that you write an authorization solution from scratch unless you really know what you are doing.
The error message tell you this:
Your variable #procedure is nil at the time that the method correct_user is called.
I'd like to give some more rights to my admin users that the regular users, here are my before_action :
# users_controller.rb
before_action :correct_user, only: [:edit, :update, :show]
before_action :admin_user, only: [:destroy, :index, :admin_toggle]
So if you're the correct user, you can : edit, update, show your own profile.
If you're an admin_user, you can see the list (index), destroy your own profile, toggle admin any user, edit/update/show your own profile (admin_user is also a correct_user).
I'd like an admin_user to be able to edit, update, show others members profiles : do I need to write specific methods or is there a trick with before_action to do ?
It seems to be like admin_user should have the rights of correct_user -with any user ID given-.
If you use somekind of specific authentication tools like Devise for ex. then I suggest implementing authorization solution with
cancancan gem. You have a specific ability file there under your models directory where you can declare access-rights for different user roles.
Makes your future code much cleaner and easier to read also.
###EDIT:
As previous answer points out then there is also a CanCan gem but as much as I know then it is not supported in Rails4. While writing this answer, CanCanCan build-status in github is marked as failing but I've been using it for a long time now in my projects and I'm happy :)
###
ability.rb example:
def secretary_abilities
can [:create, :read, :update], Person
end
def superadmin_abilities
# superadmin can do everything that secretary can
secretary_abilities
# ...and can also do destructive actions
can [:destroy], Person
end
After that you can add checks into your views like this:
<% if can? :show, Person %>
<%= link_to 'Show', person_path(#person) %>
<% end %>
I have a controller before_filter that redirects non-admin users to the root path, if they attempt to update someone else's profile:
before_filter :correct_user, only: [:edit, :update]
....
private
def correct_user
#user=User.find(params[:id])
redirect_to(root_path) unless current_user?(#user) || current_user.admin?
end
I'm using this filter in two different controllers, and I want to DRY up the code.
My specs still pass when I move the method into application_helper.rb, but I'm weary of assigning the #user variable in a helper file. Can any harm come from this? Should I at least be delegating the assignment of the variable to a private method in the helper?
Is it better to have code duplication in this instance, or is my solution safe enough?
move your correct_user method into your application controller and then you can use in any of the controller with before_filter.
Following the guide here, I added a boolean attribute to my database using a migration:
rails generate migration add_admin_to_user admin:boolean
I've configured my account to be an admin (admin = 1) via Rails console. I have a controller that I want to restrict access to certain actions (new, edit, create, and destroy) for administrators only.
I'll also have normal users, I just want to restrict access to these actions for admins only in this controller. Currently, I'm using the code:
before_filter :authenticate_user!, :only => [:new, :edit, :create, :destroy]
Which restricts access to registered users -- how do I take this a step further and require admins?
you can easily implement your own before_filter to allow access to only admin users by using the .admin? method associated with your user model. for instance:
before_filter :verify_is_admin
private
def verify_is_admin
(current_user.nil?) ? redirect_to(root_path) : (redirect_to(root_path) unless current_user.admin?)
end
You will want to define your own method in the before filter and then detect whether the user is an admin or not in that method prior to calling :authenticate_user!
before_filter :custom_method, :only => [:new, :edit, :create, :destroy]
private
def custom_method
authenticate_user!
if current_user.admin
return
else
redirect_to root_url # or whatever
end
end
You will want to do the authenticate_user! step prior to checking the current_user variable.
ian.