Rails Authorize Editor and Correct User - ruby-on-rails

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

Error: Filter chain halted as :correct_user rendered or redirected

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

Admin user allow to delete and edit other users

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

rails before_action dilemma

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

undefined method `user' for nil:NilClass when method destroy

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

Rails, duplicate code refactoring

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

Resources