I am using Pundit for authorization for my User model.
My goal is to extend this to use my AdminUser model, specifically for my admin namespace.
By default, Pundit checks for a "user" or "current_user". How can I change this to check for a "admin_user" or "current_admin_user", based on Devise?
policies/admin/admin_policy.rb (Closed system, currently looks for User instead of AdminUser)
class Admin::AdminPolicy
attr_reader :user, :record
def initialize(user, record)
# Must be logged in
raise Pundit::NotAuthorizedError, "You must be logged in to perform this action" unless user
#user = user
#record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
raise Pundit::NotAuthorizedError, "You must be logged in to perform this action" unless user
#user = user
#scope = scope
end
def resolve
scope.all
end
end
end
policies/admin/home_policy.rb (Example sub-policy of the Admin namespace)
class Admin::HomePolicy < Admin::AdminPolicy
def index?
user.present?
end
end
I think you need to define the method pundit_user on your controllers to customize it https://github.com/varvet/pundit#customize-pundit-user
def pundit_user
current_admin_user
end
Related
I am currently using Pundit as my authorization gem on my Rails app. Basically, I created this page where in if you are admin (which means if a field has the admin value set up to true) you can view this page and delete some existing user on the database viahttp://localhost:3000/dashboard/users`
I already place this on my menu:
<% if current_user.admin === true %>
<%= link_to "Users List", users_path, class: "dropdown-item #{active_class_white('/dashboard/contacts')}" %>
<% end %>
Which will hide this link if the user is not admin. However, this can be still access if the non-admin visits the url directly.
I've already set up my pundit file via application_pundit.rb file which contains the ff:
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
end
Now I don't know what to do how to prevent other users that doesn't have the admin access to view this page and be able to delete any users from the database.
Please help.
UPDATE: I created a user_policy.rb and place the ff codes:
class UserPolicy < ApplicationPolicy
def index?
user.admin?
end
def destroy?
user.admin?
end
end
But this did not work as a non-admin can still access the page. What else should I do?
In user_policy:
class UserPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def index?
user.admin?
end
end
In the context of Pundit, user is current_user so this will evalue to true if user is an admin (and to false otherwise).
And in your controller:
def index
#users = policy_scope(User)
authorize User
end
I installed Activeadmin and Pundit gems.
Added 'include Pundit' in application_controller.rb.
Defined package_policy.rb
class PackagePolicy < ApplicationPolicy
def update?
user.admin?
end
end
application_policy.rb:
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 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
end
And than I get
page isn’t redirecting properly Firefox has detected that the server is redirecting the request for this address in a way that will never complete
in my browser. Maybe, it is infinity loop or something like it.
I had some different configures of package_policy.rb,
but after added application_policy.rb - the result is always error in browser after trying to log in to Activeadmin panel.
I allowed all actions for all methods in my ApplicationPolicy.
And after I created new policies with needed permissions for my resources.
In ApplicationPolicy:
...
def index?
true
end
def show?
true
end
def create?
true
end
def new?
create?
end
def update?
true
end
def edit?
update?
end
def destroy?
true
end
...
In any other policy, for example:
...
def index?
user.admin?
end
def show?
user.admin?
end
def create?
user.admin?
end
def new?
create?
end
def update?
user.admin?
end
def edit?
update?
end
def destroy?
user.admin?
end
...
I've had a long break from my 2 years of effort in trying to learn how to use pundit in my rails app. I'm back and trying to learn how to use pundit.
I've made a completely new rails 5 app and installed pundit.
I have a user resource, an application policy and a user policy. Each has:
Users controller:
def index
# #users = User.all
#users = policy_scope(User)
end
Application Policy
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
true
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
end
User policy
class UserPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.where(user: user)
end
end
end
Then in my user index, I'm trying to follow the instructions in the pundit gem docs, by doing:
<% policy_scope(#users).each do |user| %>
I get this error:
PG::UndefinedColumn: ERROR: column users.user does not exist
LINE 1: SELECT "users".* FROM "users" WHERE "users"."user" = '566119...
^
: SELECT "users".* FROM "users" WHERE "users"."user" = '566119d2-54d8-4ab2-b7c5-f17c80b517f3' AND "users"."user" = '566119d2-54d8-4ab2-b7c5-f17c80b517f3'
Can anyone see how I'm getting off to the wrong start? I haven't even tried to define my scope in the way I want to yet, but it isn't working at this point.
class UserPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.where(user: user)
end
end
end
scope.where means the User class (user.where), because it's inside the user policy.
The user value is the current user, inherited by the application scope.
The scope example that you posted i'm guessing that you want to show only current user records. Then you can do the following as the comment above:
scope.where(id: user.try(:id))
Also because you have defined a scope in the controller
def index
# #users = User.all
#users = policy_scope(User)
end
You don't need to define another one in the view.
<% policy_scope(#users).each do |user| %>
It's one or the other. In the view you can simply do users.each do....
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 working on Authorization for my school assignment, which is a Reddit clone. I was just introduced to the Pundit Gem for Authorization on user roles, ie, Admin, Moderator, Member and Guest.
I have to make it so:
Admins and Moderators should see all posts, members should only see their own posts, and guests should see no posts.
Sign in as a normal user, and you should only see the posts you've created.
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?
# Checks if user exists and is logged in
user.present?
end
def new?
create?
end
def update?
# Checks if user is logged in, the owner or admin
user.present? && (record.user == user || user.admin?)
end
def edit?
update?
end
def destroy?
update?
end
def scope
record.class
end
end
Here is what I am working on:
This will check if a user is present, and if the user is a moderator or administrator and only grant them access to view posts. Works just like the instructions state.
post_policy.rb
class PostPolicy < ApplicationPolicy
def index?
user.present? && (user.moderator? || user.admin?)
end
end
Now if I look back at my application_policy.rb I can see this line here, "Checks if the user is logged in, the owner, or admin":
user.preset? && (record.user == user || user.admin?)
If I try to add this into my authorization of index? I will keep getting a
"NoMethodError in PostsController#index"
class PostPolicy < ApplicationPolicy
def index?
user.present? && (user.moderator? || user.admin? || record.user == user)
end
end
Thank you.
Use scopes: https://github.com/elabs/pundit#scopes
In your case PostPolicy.rb should look like this:
class PostPolicy < ApplicationPolicy
def index?
true
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin? || user.moderator?
scope.all
else
scope.where(user: user)
end
end
end
end