Comments (14)
Note this would also apply to using authorize
in a controller's index action.
from pundit.
Pundit's Scopes are a great way to determine which records a user can get an overview on in a typical index action.
# app/policies/post_policy.rb
class PostPolicy < Struct.new(:user, :post)
class Scope < Struct.new(:user, :scope)
# Admins can access all posts on the index view,
# any other user will only be able to see published ones
def resolve
if user.admin?
scope
else
scope.where(:published => true)
end
end
end
class PostsController < ApplicationController
after_filter :verify_policy_scoped, :only => :index
def index
@posts = policy_scope(Post)
end
end
Policies can be used in the index view to show or hide links to certain actions depending on the user's privileges.
# app/views/posts/index.html.erb
<% if policy(post).create? %>
<%= link_to "New post", new_post_path %>
<% end %>
More on Pundit's Scopes. All right, Brian?
from pundit.
Well I'm trying to show/hide links to the index view itself. I was trying to find out how to do something like:
# app/views/index.html.erb
<% if policy(Post).index? %>
<%= link_to "View Posts", posts_path %>
<% end %>
but the best I could get was:
# app/views/index.html.erb
<% PostPolicy.new(current_user).index? %>
<%= link_to "View Posts", posts_path %>
<% end %>
from pundit.
@posts = policy_scope(Post)
should return an ActiveRecord::Relation
. In the view you might check for if any posts could be retrieved.
# this only would make sense anywhere but on the PostsController#index page itself
<% if policy_scope(Post).any? %>
<%= link_to "View Posts", posts_path %>
<% end %>
DANGER! However, this would really only hide the link. Using the scope in the PostsController#index
action is the best way to select there which posts a user can view.
In many apps, the index page next to displaying an overview of records holds important links, such as a button that let's you create a new post. As such, I have a feeling that an index page should always be viewable (unless the app is completely locked down), and if there are no records in the scope then it simply will be a 'blank slate' with all the important links to create a first record.
Hope this helps, Brian. Ask away if you have any more questions!
from pundit.
Yah, in our app's case the index page should not be viewable at all by certain categories of users. Imagine a nav menu with various sections and section A (a link to the index action) should not be visible at all to non-admins.
from pundit.
@bdmac Hiding the link will not lock down the action for non-admins per se. They can access it by pointing the browser to the right URL.
In your case, a slight workaround might do the trick.
# app/policies/post_policy.rb
class PostPolicy < Struct.new(:user, :post)
def index?
user.admin_of?(post.account)
end
end
# app/views/controller/action.html.erb
# Build a new record (unsaved like Post.new) scoped to the current account
# This actually checks if you can call an index action on a single new post,
# and should thus be considered only a workaround.
# Using SCOPES and checking for whether any records exist in the scope
# with policy_scope(Post).any? is in my opinion strongly advisable.
<% if policy(@account.posts.build).index? %>
<%= link_to "View Posts", posts_path %>
<% end %>
from pundit.
Yah, that's not much better than if PostPolicy.new(current_user).index?
though. In fact I think I like just directly instantiating the policy more than building a throwaway Post instance to auth against. :-)
from pundit.
Thanks for the ideas though!
from pundit.
I usually just send in the class:
<% if policy(Post).index? %>
<%= link_to "View Posts", posts_path %>
<% end %>
It doesn't make a whole lot of sense, but as a feature, it's explicitly supported, so it definitely works.
from pundit.
Oh snap, I must've missed that in the docs. Didn't know it supported that. Has that always been available? Thanks Jonas!
from pundit.
Not sure if it's documented, it's in the test suite at least. Should probably add a note about this to the README.
from pundit.
And, IIRC, yes this has always worked.
from pundit.
A related question. I also need to hide a link to the index action from all users.
<% if policy(Post).index? %>
<%= link_to "View Posts", posts_path %>
<% end %>
The above doesn't work for me, because my user class is Admin instead of User (but I also have a User class). Is there any way to set the pundit_user before calling policy(Post) in the view, similar to what you can do in the controller?
from pundit.
You can ignore my question above. I think it was caused by another problem I was having with my policy file. I resolved the other issue, came back to this to try to find a solution for this problem, and the code above now works fine (getting the correct user class from the pundit_user method in the controller, as I originally expected it should).
from pundit.
Related Issues (20)
- Manually specifying policy class via an instance method does not always work HOT 1
- Singular model class name vs. Plural module name HOT 3
- Split this into two methods?
- [Request] policy_scope should not alter joined table structure HOT 2
- Policy Finder `find` does not strip namespace. HOT 2
- Support authorization error flash messages when using turbo frames and streams? HOT 6
- Git tag for v2.3.0? HOT 1
- generator fails with ruby 3.2.0 HOT 6
- "include Pundit::Authorization" undefined ? HOT 3
- Enable custom description for permit matcher
- Do not use NotImplementedError HOT 1
- Rubygems version fully support Ruby 3.2
- Hook into Rails generators (scaffold, model) to generate policy classes HOT 3
- Helper policy_scope does not accept policy_scope_class HOT 1
- README for headless section is incorrect? HOT 1
- uninitialized constant Pundit::Authorization HOT 1
- Update gem release template for new gem publish workflow
- Pundit caching isn't sensitive to current user changing HOT 3
- Support :focus for rspec permissions blocks HOT 2
- how can i authorize all controllers and actions
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pundit.