I have 2 controller, 1 for user and 1 for admin.
controllers/articles_controller.rb
class ArticlesController < ActionController::Base
...
def show
#article = Article.find(parmas[:id])
authorize #article
end
...
end
controllers/admin/articles_controller.rb
class Admin::ArticlesController < AdminController
...
def show
#article = Article.find(parmas[:id])
authorize #article
end
...
end
And i have 2 file policy
policies/article_policy.rb
class ArticlePolicy
extend ActiveSupport::Autoload
autoload :Admin
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def show?
# allow show for every user.
true
end
end
And one file policies/admin/article_policy.rb
class Admin::ArticlePolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def show?
# only show if use have role manager
user.manager?
end
end
but when i use a account user to show articles at /admin/articles/1/. It show normaly, Should is "Access denied".
How to fix this? (I use gem pundit 1.10).
Use the authorize method to pass the namespace as a parameter.
class ArticlesController < ActionController::Base
...
def show
#article = Article.find(parmas[:id])
authorize [:admin, #article]
end
...
end
Related
I have created a user controller with login and logout.
After login user should be able to give some comment in text box input and it should be saved in db.
How to associate the comment to the user. My users controller is
class UsersController < ApplicationController
def new
end
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to '/url'
else
redirect_to '/signup'
end
end
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
my urls controller is
class UrlsController < ApplicationController
def new
end
def create
url = Url.new(url_params)
url.save
redirect_to #url
end
def url_params
params.require(:url).permit(:url)
end
end
I am getting error in url_params. How it should be for a text field?
For example you need to create Comment model
class Comment < ApplicationRecord
belongs_to :user
end
class User < ApplicationRecord
has_many :comments
end
and create CommentsController
class CommentsController < ApplicationController
before_action :set_user
def create
#user.comments.create(comment_params)
redirect_to root_url
end
private
def set_user
#user = User.find(session[:user_id])
end
def comment_params
params.require(:comment).permit(:text)
end
end
How to create Policies for API-Controller's using Pundit gem?
Api controller path: /app/controllers/api/posts_controller.rb
#posts_controller.rb
class Api::PostsController < ApplicationController
def create
......
end
def update
......
end
def delete
......
end
end
I have Controller for the same and the corresponding Model
controller path: /controllers/posts_controller.rb
#posts_controller.rb
class PostsController < ApplicationController
def create
......
end
def update
......
end
def delete
......
end
end
I have created the policies for posts controller. How to create the same for API's Controller
Pundit is resource-based, not controller-based. When you call authorize and pass it a resource, Pundit cares about the action name and the resource type, but it does not care about the controller name.
Regardless of whether you call from the Api::PostsController:
# /app/controllers/api/posts_controller.rb
class Api::PostsController < ApplicationController
def create
#post = Post.find(params[:post_id])
authorize #post
end
end
or from your original PostsController:
# /app/controllers/posts_controller.rb
class PostsController < ApplicationController
def create
#post = Post.find(params[:post_id])
authorize #post
end
end
So long as #post is a member of the Post class, you can call authorize #post from the controller of a parent or child or a completely unrelated controller, it doesn't matter. In all cases Pundit will go and look for a method called create? within app/policies/post_policy:
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
#user = user
#post = post
end
def create?
user.present?
end
end
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.
I'm trying to create a show view for three roles. Admin, super user, and user. An admin should see all of the users. A super user should see only users and a user should not see anyone. When I used the commented out policy method in the resolve for else user.super_user? would give me unsupported: TrueClass error. Any suggestions are welcomed.
Users Controller
def index
#users = policy_scope(User)
authorize User
end
User Policy
class UserPolicy
attr_reader :current_user, :model
def initialize(current_user, model)
#current_user = current_user
#user = model
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else user.super_user?
scope.where(user.role = 'user' )
# scope.where(user.role != 'admin') [this would not work in the views, but it would work in rails c]
end
end
end
def index?
#current_user.admin? or #current_user.super_user?
end
end
updated Users Controller
class UsersController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
def index
#users = policy_scope(User)
end
end
Correct Answer
I figured out what I needed to do. I was calling the role incorrectly. Updated scope below.
class UserPolicy
attr_reader :current_user, :model
def initialize(current_user, model)
#current_user = current_user
#user = model
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else user.super_user?
scope.where(role: 'user' )
end
end
end
def index?
#current_user.admin? or #current_user.super_user?
end
controller
class UsersController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
def index
#users = policy_scope(User)
authorize #users
end
Your resolve method should use elsif:
# Safer option
def resolve
if user.admin?
scope.all
elsif user.super_user?
scope.where(user.role = 'user' )
else
scope.none
end
end
or not check for the super user at all and just depend on checking the authorization of the user before the result is used:
# This option is the same as the code you added to your question
# but doesn't include the unnecessary check
def resolve
if user.admin?
scope.all
else
scope.where(user.role = 'user' )
end
end
EDIT: updated to deal with the case of not being an admin or super user
So, I'm trying to use the gem pundit. I'm just trying to figure out how to have an index view for users and admins. I want to render all results for an admin and only related posts for a user. I've googled and searched on github, but I'm not find any luck. What do I have to put in my policy and controller?
original code
class PostsPolicy
attr_reader :current_user, :model
def initialize(current_user, model)
#current_user = current_user
#post = model
end
def index?
#current_user.admin?
end
end
controller
class PostsController < ApplicationController
before_filter :load_user
before_filter :authenticate_user!
after_action :verify_authorized
def index
#posts = Post.order('title').page(params[:page]).per(25)
authorize User
end
private
def load_user
#user = User.find_by_id(params[:user_id])
end
end
second update
class PostsPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else
scope.where(user: user)
end
end
end
end
controller
class PostsController < ApplicationController
before_filter :load_user
before_filter :authenticate_user!
after_action :verify_authorized
def index
#posts = policy_scope(Post).order('title').page(params[:page]).per(25)
authorize User
end
private
def load_user
#user = User.find_by_id(params[:user_id])
end
end
third update
class PostPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else
scope.where(user: user)
end
end
end
def index?
user.admin? || user.posts.count > 0
end
end
controller
class PostsController < ApplicationController
before_filter :load_user
before_filter :authenticate_user!
after_action :verify_authorized
def index
#posts = policy_scope(Post).order('title').page(params[:page]).per(25)
authorize User
end
private
def load_user
#user = User.find_by_id(params[:user_id])
end
end
** final update with working code **
class PostPolicy
attr_reader :user, :model
def initialize(user, model)
#user = user
#post = model
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else
scope.where(user: user)
end
end
end
def index?
user.admin? || user.posts.count
end
end
controller
class PostsController < ApplicationController
before_filter :load_user
before_filter :authenticate_user!
after_action :verify_authorized
def index
#posts = policy_scope(Post).order('title').page(params[:page]).per(25)
authorize Post
end
private
def load_user
#user = User.find_by_id(params[:user_id])
end
end
What you're looking for is a Scope:
class PostsPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else
scope.where(user: user)
end
end
end
end
Then in your controller
def index
#posts = policy_scope(Post).order('title').page(params[:page]).per(25)
authorize User
end
Edit
As a side note, authorize User will probably not serve you well in the long run. You're essentially creating an index policy that would need to serve every index. If you want to to authorize visibility to the index page you can still do something like this in your policy:
def index?
user.admin? || user.posts.count > 0
end
Assuming that relationship is set up, then you would call authorize Post in your index controller before your policy_scope.