I am implementing pundit and wish to restrict the user#edit and user#update actions to only the current_user
def edit
#user = current_user
authorize(#user)
end
def update
#user = current_user
authorise(#user)
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to edit_user_path
else
render 'edit'
end
end
The following is my attempted policy which (a) does not work and (b) is illogical.
class UserPolicy
attr_reader :user, :user
def initialise(user, user)
#user = user
end
def update?
true
end
alias_method :edit?, :update?
end
I have now updated my UserPolicy as per below. I have set the actions to false for testing as everything was being authorised:
class UserPolicy < ApplicationPolicy
def new?
create?
end
def create?
false
end
def edit?
update?
end
def update?
false
#user.id == record.id
end
end
However my policies are not recognised. Upon further reading I added the following to my ApplicationController:
after_filter :verify_authorized, except: :index
after_filter :verify_policy_scoped, only: :index
When I now navigate to my user#edit action I receive:
Pundit::AuthorizationNotPerformedError
First, make sure you have...
your-app/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include Pundit
end
your-app/app/policies/application_policy.rb with default permissions for common actions.
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
Then, in your UserPolicy
your-app/app/policies/section_policy.rb
class UserPolicy < ApplicationPolicy
def edit?
user.id == record.id
end
def update?
edit?
end
end
So, by default, user will be your current user and record will be the #user defined on edit and update actions.
You don't need to call authorize method explicitly. Pundit knows what to do with your #user attribute. So, your controller should be:
def edit
user
end
def update
if user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to edit_user_path
else
render 'edit'
end
end
private
def user
#user ||= User.find(params[:id])
end
you must know if you don't have a current_user method, yo will need to define a pundit_user in your application controller.
Related
I'm using the Pundit gem for the user authorizations in my Rails project. The Edit function works as I expected, just user admin and whoever created the review is able to update it. However, I can't delete them with the pundit set up.
Here's my Reviews Controller:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
def index
#reviews = Review.all
end
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
if #review.save
redirect_to review_path(#review), :alert => "Awesome! Here's your small review!"
else
error_messages(#review)
render 'new'
end
end
def show
end
def edit
authorize #review
end
def update
if #review.update(review_params)
redirect_to review_path(#review), :alert => "That's a good update!"
else
error_messages(#review)
render 'edit'
end
end
def destroy
authorize #review
#review.destroy
redirect_to reviews_path, :alert => "We'll miss that review."
end
private
def set_review
#review = Review.find_by(id: params[:id])
end
def review_params
params.require(:review).permit(:title, :content)
end
end
The Application Policy looks like this:
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def create?
new?
end
def update?
false
end
def edit?
update?
end
def destroy?
destroy?
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope.all
end
end
end
And here's my customized Review Policy file:
class ReviewPolicy < ApplicationPolicy
def edit?
user.admin? || record.user == user
end
def destroy?
user.admin? || record.user == user
end
end
In case you're wondering, I created the user_not_authorized helper method. My Application Controller looks like this.
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
protect_from_forgery with: :exception
helper_method :current_user
add_flash_types :info, :error, :warning
def logged_in?
!!current_user
end
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
def error_messages(obj)
obj.errors.messages.each do |k,v|
flash[:alert] = "#{k.to_s} #{v.first.to_s}"
end
end
private
def user_not_authorized
flash[:alert] = "Ooops sorry. You don't have access to this."
redirect_to (request.referrer || root_path)
end
end
Hope my message is clear enough and would appreciate any help. Remember, '''user.admin? || record.user == user''' works for editing but the user admins and the review creators can't delete the reviews when applying the same methods.
Please let me know if any question.
I'm still trying to wrap my head around Pundit policies. I think I'm close but I've wasted too much time trying to figure this out. My Posts policy works great, but trying to authorize comments, I am getting undefined errors...
comments_controller.rb
class CommentsController < ApplicationController
before_action :find_comment, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:comment))
#comment.user_id = current_user.id if current_user
authorize #comment
#comment.save
if #comment.save
redirect_to post_path(#post)
else
render 'new'
end
end
def edit
authorize #comment
end
def update
authorize #comment
if #comment.update(params[:comment].permit(:comment))
redirect_to post_path(#post)
else
render 'edit'
end
end
def destroy
authorize #comment
#comment.destroy
redirect_to post_path(#post)
end
private
def find_comment
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
end
end
comment_policy.rb
class CommentPolicy < ApplicationPolicy
def owned
comment.user_id == user.id
end
def create?
comment.user_id = user.id
new?
end
def new?
true
end
def update?
edit?
end
def edit?
owned
end
def destroy?
owned
end
end
The formatting and indenting is a bit off... thats not how I code I swear
class ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
#user = user
#post = post
end
def index?
false
end
def show?
scope.where(:id => post.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(user, post.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
You initialized your resource in ApplicationPolicy as #post. And since your CommentPolicy inherits from ApplicationPolicy and uses its initialization, it only has access to #post. Best option is to keep it as record:
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
#user = user
#record = record
end
## code omitted
end
class CommentPolicy < ApplicationPolicy
def owned
record.user_id == user.id
end
## code omitted
end
Basically you can call it anything you want but record makes more sense as it will be used in different policy subclasses.
I'm trying to add authorization to my rails app and want to redirect a non-user to root_url when they try to access posts/new, using rescue_from. However, there is no redirect to root or error message being displayed and I'm not sure why.
This is my application_controller.rb
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
rescue_from Pundit::NotAuthorizedError do |exception|
redirect_to root_url, alert: exception.message
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :name
end
end
This is application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
user.present?
end
def new?
create?
end
def update?
user.present? && (record.user == user || user.admin?)
end
def edit?
update?
end
def destroy?
update?
end
def scope
record.class
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
This is post_policy.rb
class PostPolicy < ApplicationPolicy
def new
#post = Post.new
authorize #post
end
end
This is posts_controller.rb
class PostsController < ApplicationController
def index
#posts = Post.all
end
def show
#ApplicationController::Find
#post = Post.find(params[:id])
end
def new
#post = Post.new
end
def create
#post = current_user.posts.build(params.require(:post).permit(:title, :body))
if #post.save
flash[:notice] = "Post was saved."
redirect_to #post
else
flash[:error] = "There was an error saving the post. Please try again."
render :new
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update_attributes(params.require(:post).permit(:title, :body))
flash[:notice] = "Post was updated."
redirect_to #post
else
flash[:error] = "There was an error saving the post. Please try again."
end
end
end
The issue was that I defined another new method in the post_policy.rb file, overwriting the new method in application_policy.rb. I also didn't include authorize #post in the new method in the posts_controller.rb file.
Hello, I'm new to ruby on rails, and currently working on an exercise where I have 3 types of users ( Admin, moderator and member). I'm using the Pundit gem with the Devise Gem.
I was asked to define Pundit scope classes to make Posts accessible according to the role of the user.
Admin and moderator can see all posts. Signed in user can see his posts only. A guest can't see the posts.
Here's the PostsController:
class PostsController < ApplicationController
def index
#posts = policy_scope(Post.all)
authorize #posts
end
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
authorize #post
end
def create
#post = current_user.posts.build(params.require(:post).permit(:title, :body))
authorize #post
if #post.save
flash[:notice] = "Post was saved"
redirect_to #post
else
flash[:error] = "There was an error saving the post. Please try again"
render :new
end
end
def edit
#post = Post.find(params[:id])
authorize #post
end
def update
#post = Post.find(params[:id])
authorize #post
if #post.update_attributes(params.require(:post).permit(:title, :body))
flash[:notice] = "Post was updated."
redirect_to #post
else
flash[:error] = "There was an error saving the post.Please try again."
render :edit
end
end
end
Here's my application policy:
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
user.present?
end
def new?
create?
end
def update?
user.present? && (record.user == user || user.admin?)
end
def edit?
update?
end
def destroy?
update?
end
def scope
record.class
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
And my post policy:
class PostPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin? || user.moderator?
scope.all
else
scope.where(:id => user.id).exists?
end
end
end
def index?
user.admin? || user.id?
end
end
Also, is there anywhere I can read or learn more about scope policies with Pundit and authorization on rails?
Make sure to always write the methods to declare the admin and the moderator in your User model if you want to work with Pundit policies.
def admin?
role == 'admin'
end
def moderator?
role == 'moderator'
end
There is a better way to define admins, moderators and members. First do:
rails g migration AddRoleToUsers role:integer
Then in your users model make an enum
enum role: [:member, :moderator, :admin] # add whatever roles you want
The enum will automatically create for each role a
.member? # checks if role is member
.member! # turns the user into a member so like current_user.member!
# and the same for all other roles.
Not sure if this really helps but hope you find it useful!
Good afternoon,
I am attempting to fix I am having with my rails application.
Heres a direct image to the issue:
http://oi62.tinypic.com/2z6v5w2.jpg
here is my topic_policy.rb
class TopicPolicy < ApplicationPolicy
def index?
true
end
def create?
user.present? && user.admin?
end
def update?
create?
end
end
Here is my application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
user.present?
end
def new?
create?
end
def update?
user.present? && (record.user == user || user.admin?)
end
def edit?
update?
end
def destroy?
update?
end
def scope
record.class
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
And here is the topics_controller.rb
class TopicsController < ApplicationController
def index
#topics = Topic.all
authorize #topics
end
def new
#topic = Topic.new
authorize #topic
end
def show
#topic = Topic.find(params[:id])
authorize #topic
end
def edit
#topic = Topic.find(params[:id])
authorize #topic
end
def create
#topic = Topic.new(params.require(:topic).permit(:name, :description, :public))
authorize #topic
if #topic.save
redirect_to #topic, notice: "Topic was saved successfully."
else
flash[:error] = "Error creating topic. Please try again."
render :new
end
end
def update
#topic = Topic.find(params[:id])
authorize #topic
if #topic.update_attributes(params.require(:topic).permit(:name, :description, :public))
redirect_to #topic
else
flash[:error] = "Error saving topic. Please try again"
render :edit
end
end
end
I am just attempting to get it to view a web page through my rails app, but I am stuck.
I was able to resolve this issue by making the admin method public, it was set to private.