In my application I have the following resources Post and User. I want a user to be able to update their own post and guest post and I want guest to be able to update other guest post but not a users. A guest is simply a non-user ( @post.user.blank
).
Post Model
belongs_to :user
# columns: user_id, name, body
end
User Model
has_many :posts
end
My controller is where I get confused because I'm not sure how to make it so updating a Post can be done by a current user or guest without allowing everyone to update anyones post. Mainly another user updating another users post.
def update
@post = Post.find(params[:id])
if @post.update(post_params)
redirect_to @post
else
render action: 'edit'
end
end
def post_params
params.require(:post).permit(:name, :body)
end
I was thinking of doing:
def update
@post = Post.find(params[:id])
if @post.user == current_user or @post.user.blank?
if @post.update(post_params)
redirect_to @post
else
render action: 'edit'
end
end
end
But I'm not sure how safe this is. Seems like a user can get to another users post.
Is this right? How could I do this?
I'll do it like this since it would be better for logic to be in model(or maybe in service object):
User has to have two types a. loggedin b. guest
class Post < AR::Base
...
...
def created_by_guest?
self.user == nil #probably created by guest if user is nil
end
def update_by_user(user, attributes)
return false if user.guest and self.created_by_guest?
#add code to try updating return true if it worked and false if did not work
end
end
in your controller
def update
@post = Post.find(params[:id])
if @post.update_by_user(current_user, update_params)
#redirect to show
else
#edit page
end
end
Also you might want to try cancan gem by Ryan bates or since it has been stagnant for a while you can use cancacnan..
You want to allow updating if one of these conditions are true:
Let's create a method in the model to check if a user is allowed to update based on passing one of those two conditions:
Post Model
belongs_to :user
# columns: user_id, name, body
def updatable_by?(user)
@user_id.nil? || user == self.user # return true for guest post OR post owner
end
end
Then, update pending the result of the check:
def update
@post = Post.find(params[:id])
if @post.updatable_by?(current_user) # <-- update if current_user is allowed
if @post.update(post_params)
redirect_to @post
else
render action: 'edit'
end
end
end
My approach is to use custom validation with 'update_user' additional attribute on memory as follows:
class Post < ActiveRecord::Base
belongs_to :user
attr_accessor :update_user
validate :validate_user_scope, on: :update
private
def validate_user_scope
if !(user.blank? || user == update_user)
errors.add(:update_user, :not_permitted)
end
end
end
class User < ActiveRecord::Base
has_many :posts
end
This merit is, of course, check logic is done under Rails validation at model level (not controller).
User's access scope (or access permission) should be one of "business logic" and "business logic" should be one of Model logic so that it would be good to write in model level, I think.
I think user2784630's check logic at the following fragment should be OK:
if @post.user == current_user or @post.user.blank?
but, if you worry about the comprehensiveness of this logic, let's write test or rspec. Following is an example of test/models/post_test.rb with fixtures:
require 'test_helper'
class PostTest < ActiveSupport::TestCase
...
# test all of posts of guest, user-A, and user-B are updated by
# guest, user-A, and user-B
test "update guest post by user_A" do
p = posts(:guest_post)
p.update_user = users(:user_A)
assert p.valid?
end
...
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.