I want to have correct users, admins, and editors be able to edit, update, and delete a blog.
I have this in my Blogs controller:
before_action :require_user, only: [:new, :create, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy, :can_edit_blog?]
and this in under private:
def can_edit_blog?
true if ((current_user.admin? || current_user.editor?) || correct_user)
end
def correct_user
#blog = current_user.blogs.find_by(id: params[:id])
redirect_to root_url if #blog.nil?
end
Right now it only allows the correct user, but not the admin or editors. I've tried using another before_action :admin_user, but that didn't seem to work.
before_action :require_creator, only: [:edit, :update, :destroy]
...
def can_edit_blog?
current_user.admin? || current_user.editor?
end
def require_creator
#blog = Blog.find(params[:id])
redirect_to root_url unless (current_user == #blog.user || can_edit_blog?)
end
I have an app that has some similar logic. Here is what my controller code would look like. Some of the private methods can be abstracted to the ApplicationController.
before_action :require_user, only: [:new, :create, :edit, :update, :destroy]
before_action :require_creator, only: [:edit, :update, :destroy]
private
def require_user
access_denied unless logged_in?
end
def logged_in?
!!current_user
end
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def access_denied
redirect_to root_path
end
def require_creator
access_denied unless logged_in? and (current_user == #blog.user || current_user.admin?)
end
You don't want to call can_edit_blog in the :only option of the before_action calling correct_user. You want to call can_edit_blog? in correct_user:
before_action :correct_user?, only: [:edit, :update, :delete]
...
private
def can_edit_blog?
current_user.admin? || current_user.editor?
end
def correct_user
#blog = current_user.blogs.find_by(id: params[:id])
if #blog.nil? or not can_edit_blog?
redirect_to root_url
end
end
OP's solution is correct, mine is not, refer to their updated question
Related
I'm working on review applications. In this app, users can post their own project and other users review to it. But I faced this error when other users try to edit their own reviews. To edit a project is only allowed to project owners. But to edit reviews should be allowed to users who wrote its review.
How can I divide this authentication?
/controllers/projects_controller.rb
class ProjectsController < ApplicationController
before_action :signed_in_user, only: [:new, :create, :edit, :update, :destroy]
before_action :set_project, only: [:show, :edit, :update]
before_action :correct_user, only: [:edit, :update]
def edit
end
def update
#project.attributes = create_params
if #project.save
redirect_to #project
else
render edit_project_path(id: #project.id)
end
end
private
def signed_in_user
unless user_signed_in?
redirect_to root_path
end
end
def set_project
#project = Project.find_by_id(params[:id])
end
def correct_user
unless current_user.projects.include?(#user)
redirect_to root_path
end
end
end
/controllers/reviews_controller.rb
class ReviewsController < ApplicationController
before_action :set_projectct, only: [:new, :create, :edit, :update]
before_action :set_review, only: [:edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
def edit
end
def update
#review.attributes = create_params
if #reviews.save
redirect_to prokect_path(id: #review.project_id)
else
redirect_to project_path(id: #review.project_id)
end
end
def set_project
#project = Project.find_by_id(params[:project_id])
end
def set_review
#review = Review.find_by_id(params[:id])
end
def correct_user
unless current_user.review.include?(#review)
redirect_to root_path
end
end
end
routes.rb
resources :projects do
resources :reviews
end
If you want to verify that the user has written the review, why not compare like this:
#review.user == current_user
By the way, this is an authorization verification, so if the user does not have the right to edit the review, you should return a 403 (Forbidden) instead of a redirect.
Gems like pundit or cancancan may help you doing that properly
I have a problem with my users-controller. Currently, an admin user can delete itself, other users can not delete themselves. In addition, each user can edit themselves.
However, I would like an admin user to delete and edit itself and others. How i've to edit the users-conroller?
users controller:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
:following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def edit
#user = User.find(params[:id])
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Nutzer gelöscht"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:name, :capacity, :resource_id, :email, :password,
:password_confirmation)
end
# Before filters
# 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
thank for the help! :)
Best regard,
Phillip
Currently, before_action :correct_user, only: [:edit, :update] prevents admin from entering edit page. Modify correct_user method to change this behaviour:
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user) || current_user.admin?
end
now only current_user can edit his own account, but only admin can delete account. Don't know how to give permission to admin to edit users because current_user function is blocking it.
users_controller.rb
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: [:index, :destroy]
...
private
...
# 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
you can add check for admin.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless (current_user?(#user) || current_user.admin?)
end
Could you not define a new method that returns true for correct user or admin?
# Confirms an admin or user.
def admin_or_correct_user
unless current_user.admin? || current_user?(#user)
redirect_to(root_url)
end
end
I want the user who created post and admin to delete but it's throwing me an 'undefined error'. I want to know why it's throwing me an undefined method error.
Here's my code from the controller:
Before actions:
before_action :set_recipe, only: [:edit, :update, :show, :like]
before_action :require_user, except: [:show, :index, :like]
before_action :require_user_like, only: [:like]
before_action :require_same_user, only: [:edit, :update]
before_action :admin_or_authorship, only: :destroy
Destroy method:
def destroy
Recipe.find(params[:id]).destroy
flash[:danger] = "Deleted"
redirect_to stories_path
end
private
def recipe_params
params.require(:recipe).permit(:name, :summary, :description)
end
def set_recipe
#recipe = Recipe.find(params[:id])
end
def require_same_user
if current_user != #recipe.user and !current_user.admin?
flash[:danger] = "You can only edit your own recipes"
redirect_to stories_path
end
end
def require_user_like
if !logged_in?
flash[:danger] = "log in to like!"
redirect_to :back
end
end
def admin_or_authorship
redirect_to stories_path unless administrator? || authorship?
end
def administrator?
current_user.admin?
end
def authorship?
#recipe.user == current_user
end
The problem is that in your before_filter admin_or_authorship, which is further calling authorship?, is saying #recipe.user .... Here #recipe is not defined so is nil by default.
You need to call before_filter set_recipe for destroy too:
before_action :set_recipe, only: [:edit, :update, :show, :like, :destroy]
Your action will become:
def destroy
#recipe.destroy
flash[:danger] = "Deleted"
redirect_to stories_path
end
How can I refactor this similar snippets of code in rails controller?
app/controllers/albums_controller.rb:58…62 < >
def set_album
if current_user.admin?
#album = User.find(params[:user_id]).albums.find(params[:id])
else
#album = current_user.albums.find(params[:id])
end
end
app/controllers/articles_controller.rb:45…49 < >
def set_article
if current_user.admin?
#article = User.find(params[:user_id]).articles.find(params[:id])
else
#article = current_user.articles.find(params[:id])
end
end
app/controllers/photos_controller.rb:55…59 < >
def set_photo
if current_user.admin?
#photo = User.find(params[:user_id]).photos.find(params[:id])
else
#photo = current_user.photos.find(params[:id])
end
end
controllers/concerns/user_resource.rb
module UserResource
extend ActiveSupport::Concern
included do
before_action :set_resource , only: [:edit, :update, :destroy]
before_action :signed_in_user, only: [:new, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
end
def set_resource
association = controller_name.classify.downcase
resource = current_user.admin? ? User.find(params[:user_id]) : current_user
resource = resource.send(association.to_s.pluralize).find(params[:id])
instance_variable_set("##{association}", resource)
end
def correct_user
association = controller_name.classify.downcase
redirect_to root_path unless admin_or_current?(instance_variable_get("##{association}").user)
end
end
then, in {photos, albums, articles}_controller.rb
include UserResource
One way to do this is to create a new controller:
class ResourceController < ApplicationController
before_filter :set_resource, only: [:show, :edit, :update, :destroy]
private
def set_resource
user = current_user.admin? ? User.find(params[:user_id]) : current_user
resource = user.send(controller_name.to_sym).find(params[:id])
instance_variable_set("##{controller_name.singularize}", resource)
end
end
then your albums_controller.rb:
class AlbumsController < ResourceController
# use #album in show, edit, update, and destroy
end
articles_controller.rb:
class ArticlesController < ResourceController
# use #article in show, edit, update, and destroy
end
photos_controller.rb:
class PhotosController < ResourceController
# use #photo in show, edit, update, and destroy
end
It would a good idea to use metaprogramming here, I mean something like:
def set_resource(association_singular) # e.g. :photo
resource = current_user.admin? ? User.find(params[:user_id]) : current_user
resource = resource.send(association.to_s.pluralize).find(params[:id]) )
instance_variable_set("##{association}", resource)
end
Then, within a controller either before_filter only: [:action] or
def action
# ...
set_resource(:photo)
# ...
end