Reputation: 1224
My policy for creating, updating & destroying "likes" requires the user to be logged in.
I have worded the policy as follows:
class LikePolicy < ApplicationPolicy
def create?
user && record.user_id == user.id
end
def update?
create?
end
def destroy?
create?
end
end
My likes#create controller action is as follows:
def create
@article = Article.find(params[:article_id])
@like = @article.likes.new(user_id: current_user.id, value: params[:value] == 1 : 1 ? -1)
authorize(@like)
@like.save
redirect_to @article
end
This works however using policy to confirm the user is logged in is illogical as the code will fail on the previous like where current_user is referenced.
Is this the accepted practice for how to authorize records that include user_id?
Upvotes: 4
Views: 5448
Reputation: 5616
Normally I would handle checking that a user is logged in with a separate authentication check. If your application mostly requires a user to be signed in, then doing an authentication check before the authorization will ensure that you always have a current_user
.
Also for creates I found I wasn't liking the idea of calling new/authorize/save just to make sure the user was creating an object that belonged to them. That's application logic that really has nothing to do with authorization. Instead for create you can simply pass in the class, Pundit doesn't mind.
Depending on whether you do the authentication check first, your authorize can simply return true, or you could check for !user.nil
.
The last thing I've found helpful with Pundit is to try and abstract logic into somewhat self documenting methods. It makes reading pundit policies easier and also allows you to abstract pieces of logic up to the ApplicationPolicy or include them via modules. In the example below the is_owner?
can easily be abstracted as it is something that is usable in many situations.
Example 1: Authenticate then Authorize
class LikesController
# Either from devise, or define in ApplicationController
before_action :authenticate_user!
def create
authorize(Like)
article = Article.find(params[:article_id])
article.likes.create(user_id: current_user.id, ...)
redirect_to article
end
end
class LikePolicy < ApplicationPolicy
def create?
true
end
def update?
is_owner?
end
def destroy?
is_owner?
end
private
def is_owner?
user == record.user
end
end
Example 2: Authorize Only
class LikesController
def create
authorize(Like)
article = Article.find(params[:article_id])
article.likes.create(user_id: current_user.id, ...)
redirect_to article
end
end
class LikePolicy < ApplicationPolicy
def create?
!user.nil?
end
def update?
is_owner?
end
def destroy?
is_owner?
end
private
def is_owner?
user && user == record.user
end
end
Upvotes: 6