Prevent access on users list on Ruby on Rails - ruby-on-rails

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

Related

Rails Pundit ActiveAdmin: page isn’t redirecting properly

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
...

Rails - Pundit - how to check for current_admin_user?

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

Rails 5 - how to use Pundit

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....

Ruby gem Pundit doesn't appear to be doing the policy correctly

I'm doing this in the User#Show view:
<% if policy(Gallery.new).create? %>
<%= link_to "Add a photo gallery for #{#user.name}", new_user_gallery_path(#user), class: 'btn btn-success' %>
<% end %>
and Admin can add galleries to any user. But nobody else can.
Here's the Gallery Policy:
class GalleryPolicy < ApplicationPolicy
def create?
user.present? && (record.user == user || user.admin?)
end
def new?
create?
end
end
Here's the 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?
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
As you can see, a user should be logged in and the record should belong to them or they should be admin for them to create galleries. What am I doing wrong?
does specifying the Policy this way
if policy(Gallery).create?
change the outcome?

How to make pundit policies more DRY?

In one of my project I started to using pundit gem and I have a very simply policy that looks like this:
class CompanyPolicy < ApplicationPolicy
def index?
true if user.is_a? Administrator
end
def new?
true if user.is_a? Administrator
end
def create?
new?
end
def edit?
true if user.is_a? Administrator
end
def update?
edit?
end
end
And the question is how can I avoid repeating this:
true if user.is_a? Administrator
I do trick which looks like this:
class ApplicationPolicy
private
def self.permit_owner_to(*actions)
actions.each do |action|
define_method("#{action}?") do
owner?
end
end
end
def owner?
# owner logic
end
end
And used it in other policies
class ItemPolicy < ApplicationPolicy
permit_owner_to :show, :update, :destroy, :confirm
end
I don't actually think you need to remove this. By repeating this you are explicitly saying that this user must be an administrator to access this method. If you did want to though, you could just create a private method.
class CompanyPolicy < ApplicationPolicy
def index?
admin?
end
def new?
admin?
end
def create?
new?
end
def edit?
admin?
end
def update?
edit?
end
private
def admin?
user.is_a? Administrator
end
end
Guess this is a matter of personal preference.
You could use alias_method.
class CompanyPolicy < ApplicationPolicy
def index?
user.is_a? Administrator
end
alias_method :create?, :index?
alias_method :update?, :index?
end
You have a base class ApplicationPolicy which probably already contains:
def new?
create?
end
def edit?
update?
end
so you don't need to repeat these methods in your subclass.
.is_a? returns true or false so no need to explicitly return true if true.
That's a lot more succinct eh? :)
I combined answers from above and came up with the following:
class ApplicationPolicy
attr_reader :user
def initialize(user)
#user = user
end
def self.permit(roles, options)
return if options[:to].none?
options[:to].each do |action|
define_method("#{action}?") do
return #user.roles? Array.wrap(roles) if options[:when].blank?
send(options[:when]) and #user.roles? Array.wrap(roles)
end
end
end
end
which allows one to use it like this:
class CommentPolicy < ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#record = record
super(user)
end
permit %i[admin member], to: %i[show edit destroy update], when: :created_by_user
def created_by_user
#record.user == #user
end
end
and
permit :admin, to: %i[index update edit]
works as well
my roles method from user model looks like:
def roles?(user_roles)
user_roles.each do |role|
return true if role?(role)
end
false
end
def role?(role)
roles.any? { |r| r.name.underscore.to_sym == role }
end

Resources