pundit-2.0.0/ 0000755 0000041 0000041 00000000000 13337760034 013052 5 ustar www-data www-data pundit-2.0.0/.travis.yml 0000644 0000041 0000041 00000000630 13337760034 015162 0 ustar www-data www-data language: ruby
sudo: false
before_install:
- gem update --system
- gem install bundler
matrix:
include:
- rvm: 2.5.1
script: bundle exec rake rubocop # ONLY lint once, first
- rvm: 2.1
- rvm: 2.2.8
- rvm: 2.3.5
- rvm: 2.4.2
- rvm: 2.5.1
- rvm: jruby-9.1.8.0
env:
- JRUBY_OPTS="--debug"
- rvm: jruby-9.2.0.0
env:
- JRUBY_OPTS="--debug"
pundit-2.0.0/README.md 0000644 0000041 0000041 00000054712 13337760034 014342 0 ustar www-data www-data # Pundit
[](https://travis-ci.org/varvet/pundit)
[](https://codeclimate.com/github/varvet/pundit)
[](http://inch-ci.org/github/varvet/pundit)
[](http://badge.fury.io/rb/pundit)
Pundit provides a set of helpers which guide you in leveraging regular Ruby
classes and object oriented design patterns to build a simple, robust and
scaleable authorization system.
Links:
- [API documentation](http://www.rubydoc.info/gems/pundit)
- [Source Code](https://github.com/varvet/pundit)
- [Contributing](https://github.com/varvet/pundit/blob/master/CONTRIBUTING.md)
- [Code of Conduct](https://github.com/varvet/pundit/blob/master/CODE_OF_CONDUCT.md)
Sponsored by:
[
](https://www.varvet.com)
## Installation
``` ruby
gem "pundit"
```
Include Pundit in your application controller:
``` ruby
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery
end
```
Optionally, you can run the generator, which will set up an application policy
with some useful defaults for you:
``` sh
rails g pundit:install
```
After generating your application policy, restart the Rails server so that Rails
can pick up any classes in the new `app/policies/` directory.
## Policies
Pundit is focused around the notion of policy classes. We suggest that you put
these classes in `app/policies`. This is a simple example that allows updating
a post if the user is an admin, or if the post is unpublished:
``` ruby
class PostPolicy
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def update?
user.admin? or not post.published?
end
end
```
As you can see, this is just a plain Ruby class. Pundit makes the following
assumptions about this class:
- The class has the same name as some kind of model class, only suffixed
with the word "Policy".
- The first argument is a user. In your controller, Pundit will call the
`current_user` method to retrieve what to send into this argument
- The second argument is some kind of model object, whose authorization
you want to check. This does not need to be an ActiveRecord or even
an ActiveModel object, it can be anything really.
- The class implements some kind of query method, in this case `update?`.
Usually, this will map to the name of a particular controller action.
That's it really.
Usually you'll want to inherit from the application policy created by the
generator, or set up your own base class to inherit from:
``` ruby
class PostPolicy < ApplicationPolicy
def update?
user.admin? or not record.published?
end
end
```
In the generated `ApplicationPolicy`, the model object is called `record`.
Supposing that you have an instance of class `Post`, Pundit now lets you do
this in your controller:
``` ruby
def update
@post = Post.find(params[:id])
authorize @post
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
```
The authorize method automatically infers that `Post` will have a matching
`PostPolicy` class, and instantiates this class, handing in the current user
and the given record. It then infers from the action name, that it should call
`update?` on this instance of the policy. In this case, you can imagine that
`authorize` would have done something like this:
``` ruby
unless PostPolicy.new(current_user, @post).update?
raise Pundit::NotAuthorizedError, "not allowed to update? this #{@post.inspect}"
end
```
You can pass a second argument to `authorize` if the name of the permission you
want to check doesn't match the action name. For example:
``` ruby
def publish
@post = Post.find(params[:id])
authorize @post, :update?
@post.publish!
redirect_to @post
end
```
You can pass an argument to override the policy class if necessary. For example:
```ruby
def create
@publication = find_publication # assume this method returns any model that behaves like a publication
# @publication.class => Post
authorize @publication, policy_class: PublicationPolicy
@publication.publish!
redirect_to @publication
end
```
If you don't have an instance for the first argument to `authorize`, then you can pass
the class. For example:
Policy:
```ruby
class PostPolicy < ApplicationPolicy
def admin_list?
user.admin?
end
end
```
Controller:
```ruby
def admin_list
authorize Post # we don't have a particular post to authorize
# Rest of controller action
end
```
`authorize` returns the object passed to it, so you can chain it like this:
Controller:
```ruby
def show
@user = authorize User.find(params[:id])
end
```
You can easily get a hold of an instance of the policy through the `policy`
method in both the view and controller. This is especially useful for
conditionally showing links or buttons in the view:
``` erb
<% if policy(@post).update? %>
<%= link_to "Edit post", edit_post_path(@post) %>
<% end %>
```
## Headless policies
Given there is a policy without a corresponding model / ruby class,
you can retrieve it by passing a symbol.
```ruby
# app/policies/dashboard_policy.rb
class DashboardPolicy < Struct.new(:user, :dashboard)
# ...
end
```
```ruby
# In controllers
authorize :dashboard, :show?
```
```erb
# In views
<% if policy(:dashboard).show? %>
<%= link_to 'Dashboard', dashboard_path %>
<% end %>
```
## Scopes
Often, you will want to have some kind of view listing records which a
particular user has access to. When using Pundit, you are expected to
define a class called a policy scope. It can look something like this:
``` ruby
class PostPolicy < ApplicationPolicy
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(published: true)
end
end
end
def update?
user.admin? or not record.published?
end
end
```
Pundit makes the following assumptions about this class:
- The class has the name `Scope` and is nested under the policy class.
- The first argument is a user. In your controller, Pundit will call the
`current_user` method to retrieve what to send into this argument.
- The second argument is a scope of some kind on which to perform some kind of
query. It will usually be an ActiveRecord class or a
`ActiveRecord::Relation`, but it could be something else entirely.
- Instances of this class respond to the method `resolve`, which should return
some kind of result which can be iterated over. For ActiveRecord classes,
this would usually be an `ActiveRecord::Relation`.
You'll probably want to inherit from the application policy scope generated by the
generator, or create your own base class to inherit from:
``` ruby
class PostPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
scope.all
else
scope.where(published: true)
end
end
end
def update?
user.admin? or not record.published?
end
end
```
You can now use this class from your controller via the `policy_scope` method:
``` ruby
def index
@posts = policy_scope(Post)
end
def show
@post = policy_scope(Post).find(params[:id])
end
```
Like with the authorize method, you can also override the policy scope class:
``` ruby
def index
# publication_class => Post
@publications = policy_scope(publication_class, policy_scope_class: PublicationPolicy::Scope)
end
```
Just as with your policy, this will automatically infer that you want to use
the `PostPolicy::Scope` class, it will instantiate this class and call
`resolve` on the instance. In this case it is a shortcut for doing:
``` ruby
def index
@posts = PostPolicy::Scope.new(current_user, Post).resolve
end
```
You can, and are encouraged to, use this method in views:
``` erb
<% policy_scope(@user.posts).each do |post| %>
<%= link_to post.title, post_path(post) %>
<% end %>
```
## Ensuring policies and scopes are used
When you are developing an application with Pundit it can be easy to forget to
authorize some action. People are forgetful after all. Since Pundit encourages
you to add the `authorize` call manually to each controller action, it's really
easy to miss one.
Thankfully, Pundit has a handy feature which reminds you in case you forget.
Pundit tracks whether you have called `authorize` anywhere in your controller
action. Pundit also adds a method to your controllers called
`verify_authorized`. This method will raise an exception if `authorize` has not
yet been called. You should run this method in an `after_action` hook to ensure
that you haven't forgotten to authorize the action. For example:
``` ruby
class ApplicationController < ActionController::Base
include Pundit
after_action :verify_authorized
end
```
Likewise, Pundit also adds `verify_policy_scoped` to your controller. This
will raise an exception similar to `verify_authorized`. However, it tracks
if `policy_scope` is used instead of `authorize`. This is mostly useful for
controller actions like `index` which find collections with a scope and don't
authorize individual instances.
``` ruby
class ApplicationController < ActionController::Base
include Pundit
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index
end
```
**This verification mechanism only exists to aid you while developing your
application, so you don't forget to call `authorize`. It is not some kind of
failsafe mechanism or authorization mechanism. You should be able to remove
these filters without affecting how your app works in any way.**
Some people have found this feature confusing, while many others
find it extremely helpful. If you fall into the category of people who find it
confusing then you do not need to use it. Pundit will work just fine without
using `verify_authorized` and `verify_policy_scoped`.
### Conditional verification
If you're using `verify_authorized` in your controllers but need to
conditionally bypass verification, you can use `skip_authorization`. For
bypassing `verify_policy_scoped`, use `skip_policy_scope`. These are useful
in circumstances where you don't want to disable verification for the
entire action, but have some cases where you intend to not authorize.
```ruby
def show
record = Record.find_by(attribute: "value")
if record.present?
authorize record
else
skip_authorization
end
end
```
## Manually specifying policy classes
Sometimes you might want to explicitly declare which policy to use for a given
class, instead of letting Pundit infer it. This can be done like so:
``` ruby
class Post
def self.policy_class
PostablePolicy
end
end
```
## Just plain old Ruby
As you can see, Pundit doesn't do anything you couldn't have easily done
yourself. It's a very small library, it just provides a few neat helpers.
Together these give you the power of building a well structured, fully working
authorization system without using any special DSLs or funky syntax or
anything.
Remember that all of the policy and scope classes are just plain Ruby classes,
which means you can use the same mechanisms you always use to DRY things up.
Encapsulate a set of permissions into a module and include them in multiple
policies. Use `alias_method` to make some permissions behave the same as
others. Inherit from a base set of permissions. Use metaprogramming if you
really have to.
## Generator
Use the supplied generator to generate policies:
``` sh
rails g pundit:policy post
```
## Closed systems
In many applications, only logged in users are really able to do anything. If
you're building such a system, it can be kind of cumbersome to check that the
user in a policy isn't `nil` for every single permission. Aside from policies,
you can add this check to the base class for scopes.
We suggest that you define a filter that redirects unauthenticated users to the
login page. As a secondary defence, if you've defined an ApplicationPolicy, it
might be a good idea to raise an exception if somehow an unauthenticated user
got through. This way you can fail more gracefully.
``` ruby
class ApplicationPolicy
def initialize(user, record)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
@user = user
@record = record
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
@user = user
@scope = scope
end
end
end
```
## NilClassPolicy
To support a [null object pattern](https://en.wikipedia.org/wiki/Null_Object_pattern)
you may find that you want to implement a `NilClassPolicy`. This might be useful
where you want to extend your ApplicationPolicy to allow some tolerance of, for
example, associations which might be `nil`.
```ruby
class NilClassPolicy < ApplicationPolicy
class Scope < Scope
def resolve
raise Pundit::NotDefinedError, "Cannot scope NilClass"
end
end
def show?
false # Nobody can see nothing
end
end
```
## Rescuing a denied Authorization in Rails
Pundit raises a `Pundit::NotAuthorizedError` you can
[rescue_from](http://guides.rubyonrails.org/action_controller_overview.html#rescue-from)
in your `ApplicationController`. You can customize the `user_not_authorized`
method in every controller.
```ruby
class ApplicationController < ActionController::Base
protect_from_forgery
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized
flash[:alert] = "You are not authorized to perform this action."
redirect_to(request.referrer || root_path)
end
end
```
Alternatively, you can globally handle Pundit::NotAuthorizedError's by having rails handle them as a 403 error and serving a 403 error page. Add the following to application.rb:
```config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden```
## Creating custom error messages
`NotAuthorizedError`s provide information on what query (e.g. `:create?`), what
record (e.g. an instance of `Post`), and what policy (e.g. an instance of
`PostPolicy`) caused the error to be raised.
One way to use these `query`, `record`, and `policy` properties is to connect
them with `I18n` to generate error messages. Here's how you might go about doing
that.
```ruby
class ApplicationController < ActionController::Base
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized(exception)
policy_name = exception.policy.class.to_s.underscore
flash[:error] = t "#{policy_name}.#{exception.query}", scope: "pundit", default: :default
redirect_to(request.referrer || root_path)
end
end
```
```yaml
en:
pundit:
default: 'You cannot perform this action.'
post_policy:
update?: 'You cannot edit this post!'
create?: 'You cannot create posts!'
```
Of course, this is just an example. Pundit is agnostic as to how you implement
your error messaging.
## Manually retrieving policies and scopes
Sometimes you want to retrieve a policy for a record outside the controller or
view. For example when you delegate permissions from one policy to another.
You can easily retrieve policies and scopes like this:
``` ruby
Pundit.policy!(user, post)
Pundit.policy(user, post)
Pundit.policy_scope!(user, Post)
Pundit.policy_scope(user, Post)
```
The bang methods will raise an exception if the policy does not exist, whereas
those without the bang will return nil.
## Customize Pundit user
In some cases your controller might not have access to `current_user`, or your
`current_user` is not the method that should be invoked by Pundit. Simply
define a method in your controller called `pundit_user`.
```ruby
def pundit_user
User.find_by_other_means
end
```
## Policy Namespacing
In some cases it might be helpful to have multiple policies that serve different contexts for a
resource. A prime example of this is the case where User policies differ from Admin policies. To
authorize with a namespaced policy, pass the namespace into the `authorize` helper in an array:
```ruby
authorize(post) # => will look for a PostPolicy
authorize([:admin, post]) # => will look for an Admin::PostPolicy
authorize([:foo, :bar, post]) # => will look for a Foo::Bar::PostPolicy
policy_scope(Post) # => will look for a PostPolicy::Scope
policy_scope([:admin, Post]) # => will look for an Admin::PostPolicy::Scope
policy_scope([:foo, :bar, Post]) # => will look for a Foo::Bar::PostPolicy::Scope
```
If you are using namespaced policies for something like Admin views, it can be useful to
override the `policy_scope` and `authorize` helpers in your `AdminController` to automatically
apply the namespacing:
```ruby
class AdminController < ApplicationController
def policy_scope(scope)
super([:admin, scope])
end
def authorize(record, query = nil)
super([:admin, record], query)
end
end
class Admin::PostController < AdminController
def index
policy_scope(Post)
end
def show
post = Post.find(params[:id])
authorize(post)
end
end
```
## Additional context
Pundit strongly encourages you to model your application in such a way that the
only context you need for authorization is a user object and a domain model that
you want to check authorization for. If you find yourself needing more context than
that, consider whether you are authorizing the right domain model, maybe another
domain model (or a wrapper around multiple domain models) can provide the context
you need.
Pundit does not allow you to pass additional arguments to policies for precisely
this reason.
However, in very rare cases, you might need to authorize based on more context than just
the currently authenticated user. Suppose for example that authorization is dependent
on IP address in addition to the authenticated user. In that case, one option is to
create a special class which wraps up both user and IP and passes it to the policy.
``` ruby
class UserContext
attr_reader :user, :ip
def initialize(user, ip)
@user = user
@ip = ip
end
end
class ApplicationController
include Pundit
def pundit_user
UserContext.new(current_user, request.ip)
end
end
```
## Strong parameters
In Rails 4 (or Rails 3.2 with the
[strong_parameters](https://github.com/rails/strong_parameters) gem),
mass-assignment protection is handled in the controller. With Pundit you can
control which attributes a user has access to update via your policies. You can
set up a `permitted_attributes` method in your policy like this:
```ruby
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def permitted_attributes
if user.admin? || user.owner_of?(post)
[:title, :body, :tag_list]
else
[:tag_list]
end
end
end
```
You can now retrieve these attributes from the policy:
```ruby
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update
@post = Post.find(params[:id])
if @post.update_attributes(post_params)
redirect_to @post
else
render :edit
end
end
private
def post_params
params.require(:post).permit(policy(@post).permitted_attributes)
end
end
```
However, this is a bit cumbersome, so Pundit provides a convenient helper method:
```ruby
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update
@post = Post.find(params[:id])
if @post.update_attributes(permitted_attributes(@post))
redirect_to @post
else
render :edit
end
end
end
```
If you want to permit different attributes based on the current action, you can define a `permitted_attributes_for_#{action}` method on your policy:
```ruby
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def permitted_attributes_for_create
[:title, :body]
end
def permitted_attributes_for_edit
[:body]
end
end
```
If you have defined an action-specific method on your policy for the current action, the `permitted_attributes` helper will call it instead of calling `permitted_attributes` on your controller.
If you need to fetch parameters based on namespaces different from the suggested one, override the below method, in your controller, and return an instance of `ActionController::Parameters`.
```ruby
def pundit_params_for(record)
params.require(PolicyFinder.new(record).param_key)
end
```
For example:
```ruby
# If you don't want to use require
def pundit_params_for(record)
params.fetch(PolicyFinder.new(record).param_key, {})
end
# If you are using something like the JSON API spec
def pundit_params_for(_record)
params.fetch(:data, {}).fetch(:attributes, {})
end
```
## RSpec
### Policy Specs
Pundit includes a mini-DSL for writing expressive tests for your policies in RSpec.
Require `pundit/rspec` in your `spec_helper.rb`:
``` ruby
require "pundit/rspec"
```
Then put your policy specs in `spec/policies`, and make them look somewhat like this:
``` ruby
describe PostPolicy do
subject { described_class }
permissions :update?, :edit? do
it "denies access if post is published" do
expect(subject).not_to permit(User.new(admin: false), Post.new(published: true))
end
it "grants access if post is published and user is an admin" do
expect(subject).to permit(User.new(admin: true), Post.new(published: true))
end
it "grants access if post is unpublished" do
expect(subject).to permit(User.new(admin: false), Post.new(published: false))
end
end
end
```
An alternative approach to Pundit policy specs is scoping them to a user context as outlined in this
[excellent post](http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/) and implemented in the third party [pundit-matchers](https://github.com/chrisalley/pundit-matchers) gem.
### Scope Specs
Pundit does not provide a DSL for testing scopes. Just test it like a regular Ruby class!
# External Resources
- [RailsApps Example Application: Pundit and Devise](https://github.com/RailsApps/rails-devise-pundit)
- [Migrating to Pundit from CanCan](http://blog.carbonfive.com/2013/10/21/migrating-to-pundit-from-cancan/)
- [Testing Pundit Policies with RSpec](http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/)
- [Using Pundit outside of a Rails controller](https://github.com/varvet/pundit/pull/136)
- [Straightforward Rails Authorization with Pundit](http://www.sitepoint.com/straightforward-rails-authorization-with-pundit/)
# License
Licensed under the MIT license, see the separate LICENSE.txt file.
pundit-2.0.0/spec/ 0000755 0000041 0000041 00000000000 13337760034 014004 5 ustar www-data www-data pundit-2.0.0/spec/policy_finder_spec.rb 0000644 0000041 0000041 00000006272 13337760034 020200 0 ustar www-data www-data require "spec_helper"
describe Pundit::PolicyFinder do
let(:user) { double }
let(:post) { Post.new(user) }
let(:comment) { CommentFourFiveSix.new }
let(:article) { Article.new }
describe "#scope" do
subject { described_class.new(post) }
it "returns a policy scope" do
expect(subject.scope).to eq PostPolicy::Scope
end
context "policy is nil" do
it "returns nil" do
allow(subject).to receive(:policy).and_return nil
expect(subject.scope).to eq nil
end
end
end
describe "#policy" do
subject { described_class.new(post) }
it "returns a policy" do
expect(subject.policy).to eq PostPolicy
end
context "with a string" do
it "returns a policy" do
allow(subject).to receive(:find).and_return "PostPolicy"
expect(subject.policy).to eq PostPolicy
end
end
context "with a class" do
it "returns a policy" do
allow(subject).to receive(:find).and_return PostPolicy
expect(subject.policy).to eq PostPolicy
end
end
context "with nil" do
it "returns nil" do
allow(subject).to receive(:find).and_return nil
expect(subject.policy).to eq nil
end
end
context "with a string that can't be constantized" do
it "returns nil" do
allow(subject).to receive(:find).and_return "FooPolicy"
expect(subject.policy).to eq nil
end
end
end
describe "#scope!" do
context "@object is nil" do
subject { described_class.new(nil) }
it "returns the NilClass policy's scope class" do
expect(subject.scope!).to eq NilClassPolicy::Scope
end
end
context "@object is defined" do
subject { described_class.new(post) }
it "returns the scope" do
expect(subject.scope!).to eq PostPolicy::Scope
end
end
end
describe "#param_key" do
context "object responds to model_name" do
subject { described_class.new(comment) }
it "returns the param_key" do
expect(subject.object).to respond_to(:model_name)
expect(subject.param_key).to eq "comment_four_five_six"
end
end
context "object is a class" do
subject { described_class.new(Article) }
it "returns the param_key" do
expect(subject.object).not_to respond_to(:model_name)
expect(subject.object).to be_a Class
expect(subject.param_key).to eq "article"
end
end
context "object is an instance of a class" do
subject { described_class.new(article) }
it "returns the param_key" do
expect(subject.object).not_to respond_to(:model_name)
expect(subject.object).not_to be_a Class
expect(subject.object).to be_an_instance_of Article
expect(subject.param_key).to eq "article"
end
end
context "object is an array" do
subject { described_class.new([:project, article]) }
it "returns the param_key for the last element of the array" do
expect(subject.object).not_to respond_to(:model_name)
expect(subject.object).not_to be_a Class
expect(subject.object).to be_an_instance_of Array
expect(subject.param_key).to eq "article"
end
end
end
end
pundit-2.0.0/spec/policies/ 0000755 0000041 0000041 00000000000 13337760034 015613 5 ustar www-data www-data pundit-2.0.0/spec/policies/post_policy_spec.rb 0000644 0000041 0000041 00000001001 13337760034 021506 0 ustar www-data www-data require "spec_helper"
describe PostPolicy do
let(:user) { double }
let(:own_post) { double(user: user) }
let(:other_post) { double(user: double) }
subject { described_class }
permissions :update?, :show? do
it "is successful when all permissions match" do
should permit(user, own_post)
end
it "fails when any permissions do not match" do
expect do
should permit(user, other_post)
end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
end
end
end
pundit-2.0.0/spec/pundit_spec.rb 0000644 0000041 0000041 00000051500 13337760034 016647 0 ustar www-data www-data require "spec_helper"
describe Pundit do
let(:user) { double }
let(:post) { Post.new(user) }
let(:customer_post) { Customer::Post.new(user) }
let(:post_four_five_six) { PostFourFiveSix.new(user) }
let(:comment) { Comment.new }
let(:comment_four_five_six) { CommentFourFiveSix.new }
let(:article) { Article.new }
let(:controller) { Controller.new(user, "update", {}) }
let(:artificial_blog) { ArtificialBlog.new }
let(:article_tag) { ArticleTag.new }
let(:comments_relation) { CommentsRelation.new }
let(:empty_comments_relation) { CommentsRelation.new(true) }
let(:tag_four_five_six) { ProjectOneTwoThree::TagFourFiveSix.new(user) }
let(:avatar_four_five_six) { ProjectOneTwoThree::AvatarFourFiveSix.new }
let(:wiki) { Wiki.new }
describe ".authorize" do
it "infers the policy and authorizes based on it" do
expect(Pundit.authorize(user, post, :update?)).to be_truthy
end
it "can be given a different policy class" do
expect(Pundit.authorize(user, post, :create?, policy_class: PublicationPolicy)).to be_truthy
end
it "works with anonymous class policies" do
expect(Pundit.authorize(user, article_tag, :show?)).to be_truthy
expect { Pundit.authorize(user, article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
end
it "raises an error with a query and action" do
# rubocop:disable Style/MultilineBlockChain
expect do
Pundit.authorize(user, post, :destroy?)
end.to raise_error(Pundit::NotAuthorizedError, "not allowed to destroy? this #") do |error|
expect(error.query).to eq :destroy?
expect(error.record).to eq post
expect(error.policy).to eq Pundit.policy(user, post)
end
# rubocop:enable Style/MultilineBlockChain
end
it "raises an error with a invalid policy constructor" do
expect do
Pundit.authorize(user, wiki, :update?)
end.to raise_error(Pundit::InvalidConstructorError, "Invalid # constructor is called")
end
end
describe ".policy_scope" do
it "returns an instantiated policy scope given a plain model class" do
expect(Pundit.policy_scope(user, Post)).to eq :published
end
it "returns an instantiated policy scope given an active model class" do
expect(Pundit.policy_scope(user, Comment)).to eq CommentScope.new(Comment)
end
it "returns an instantiated policy scope given an active record relation" do
expect(Pundit.policy_scope(user, comments_relation)).to eq CommentScope.new(comments_relation)
end
it "returns an instantiated policy scope given an empty active record relation" do
expect(Pundit.policy_scope(user, empty_comments_relation)).to eq CommentScope.new(empty_comments_relation)
end
it "returns an instantiated policy scope given an array of a symbol and plain model class" do
expect(Pundit.policy_scope(user, [:project, Post])).to eq :read
end
it "returns an instantiated policy scope given an array of a symbol and active model class" do
expect(Pundit.policy_scope(user, [:project, Comment])).to eq Comment
end
it "returns nil if the given policy scope can't be found" do
expect(Pundit.policy_scope(user, Article)).to be_nil
end
it "raises an exception if nil object given" do
expect { Pundit.policy_scope(user, nil) }.to raise_error(Pundit::NotDefinedError)
end
it "raises an error with a invalid policy scope constructor" do
expect do
Pundit.policy_scope(user, Wiki)
end.to raise_error(Pundit::InvalidConstructorError, "Invalid # constructor is called")
end
end
describe ".policy_scope!" do
it "returns an instantiated policy scope given a plain model class" do
expect(Pundit.policy_scope!(user, Post)).to eq :published
end
it "returns an instantiated policy scope given an active model class" do
expect(Pundit.policy_scope!(user, Comment)).to eq CommentScope.new(Comment)
end
it "throws an exception if the given policy scope can't be found" do
expect { Pundit.policy_scope!(user, Article) }.to raise_error(Pundit::NotDefinedError)
end
it "throws an exception if the given policy scope can't be found" do
expect { Pundit.policy_scope!(user, ArticleTag) }.to raise_error(Pundit::NotDefinedError)
end
it "throws an exception if the given policy scope is nil" do
expect do
Pundit.policy_scope!(user, nil)
end.to raise_error(Pundit::NotDefinedError, "Cannot scope NilClass")
end
it "returns an instantiated policy scope given an array of a symbol and plain model class" do
expect(Pundit.policy_scope!(user, [:project, Post])).to eq :read
end
it "returns an instantiated policy scope given an array of a symbol and active model class" do
expect(Pundit.policy_scope!(user, [:project, Comment])).to eq Comment
end
it "raises an error with a invalid policy scope constructor" do
expect do
Pundit.policy_scope(user, Wiki)
end.to raise_error(Pundit::InvalidConstructorError, "Invalid # constructor is called")
end
end
describe ".policy" do
it "returns an instantiated policy given a plain model instance" do
policy = Pundit.policy(user, post)
expect(policy.user).to eq user
expect(policy.post).to eq post
end
it "returns an instantiated policy given an active model instance" do
policy = Pundit.policy(user, comment)
expect(policy.user).to eq user
expect(policy.comment).to eq comment
end
it "returns an instantiated policy given a plain model class" do
policy = Pundit.policy(user, Post)
expect(policy.user).to eq user
expect(policy.post).to eq Post
end
it "returns an instantiated policy given an active model class" do
policy = Pundit.policy(user, Comment)
expect(policy.user).to eq user
expect(policy.comment).to eq Comment
end
it "returns an instantiated policy given a symbol" do
policy = Pundit.policy(user, :criteria)
expect(policy.class).to eq CriteriaPolicy
expect(policy.user).to eq user
expect(policy.criteria).to eq :criteria
end
it "returns an instantiated policy given an array of symbols" do
policy = Pundit.policy(user, %i[project criteria])
expect(policy.class).to eq Project::CriteriaPolicy
expect(policy.user).to eq user
expect(policy.criteria).to eq :criteria
end
it "returns an instantiated policy given an array of a symbol and plain model instance" do
policy = Pundit.policy(user, [:project, post])
expect(policy.class).to eq Project::PostPolicy
expect(policy.user).to eq user
expect(policy.post).to eq post
end
it "returns an instantiated policy given an array of a symbol and a model instance with policy_class override" do
policy = Pundit.policy(user, [:project, customer_post])
expect(policy.class).to eq Project::PostPolicy
expect(policy.user).to eq user
expect(policy.post).to eq customer_post
end
it "returns an instantiated policy given an array of a symbol and an active model instance" do
policy = Pundit.policy(user, [:project, comment])
expect(policy.class).to eq Project::CommentPolicy
expect(policy.user).to eq user
expect(policy.comment).to eq comment
end
it "returns an instantiated policy given an array of a symbol and a plain model class" do
policy = Pundit.policy(user, [:project, Post])
expect(policy.class).to eq Project::PostPolicy
expect(policy.user).to eq user
expect(policy.post).to eq Post
end
it "raises an error with a invalid policy constructor" do
expect do
Pundit.policy(user, Wiki)
end.to raise_error(Pundit::InvalidConstructorError, "Invalid # constructor is called")
end
it "returns an instantiated policy given an array of a symbol and an active model class" do
policy = Pundit.policy(user, [:project, Comment])
expect(policy.class).to eq Project::CommentPolicy
expect(policy.user).to eq user
expect(policy.comment).to eq Comment
end
it "returns an instantiated policy given an array of a symbol and a class with policy_class override" do
policy = Pundit.policy(user, [:project, Customer::Post])
expect(policy.class).to eq Project::PostPolicy
expect(policy.user).to eq user
expect(policy.post).to eq Customer::Post
end
it "returns correct policy class for an array of a multi-word symbols" do
policy = Pundit.policy(user, %i[project_one_two_three criteria_four_five_six])
expect(policy.class).to eq ProjectOneTwoThree::CriteriaFourFiveSixPolicy
end
it "returns correct policy class for an array of a multi-word symbol and a multi-word plain model instance" do
policy = Pundit.policy(user, [:project_one_two_three, post_four_five_six])
expect(policy.class).to eq ProjectOneTwoThree::PostFourFiveSixPolicy
end
it "returns correct policy class for an array of a multi-word symbol and a multi-word active model instance" do
policy = Pundit.policy(user, [:project_one_two_three, comment_four_five_six])
expect(policy.class).to eq ProjectOneTwoThree::CommentFourFiveSixPolicy
end
it "returns correct policy class for an array of a multi-word symbol and a multi-word plain model class" do
policy = Pundit.policy(user, [:project_one_two_three, PostFourFiveSix])
expect(policy.class).to eq ProjectOneTwoThree::PostFourFiveSixPolicy
end
it "returns correct policy class for an array of a multi-word symbol and a multi-word active model class" do
policy = Pundit.policy(user, [:project_one_two_three, CommentFourFiveSix])
expect(policy.class).to eq ProjectOneTwoThree::CommentFourFiveSixPolicy
end
it "returns correct policy class for a multi-word scoped plain model class" do
policy = Pundit.policy(user, ProjectOneTwoThree::TagFourFiveSix)
expect(policy.class).to eq ProjectOneTwoThree::TagFourFiveSixPolicy
end
it "returns correct policy class for a multi-word scoped plain model instance" do
policy = Pundit.policy(user, tag_four_five_six)
expect(policy.class).to eq ProjectOneTwoThree::TagFourFiveSixPolicy
end
it "returns correct policy class for a multi-word scoped active model class" do
policy = Pundit.policy(user, ProjectOneTwoThree::AvatarFourFiveSix)
expect(policy.class).to eq ProjectOneTwoThree::AvatarFourFiveSixPolicy
end
it "returns correct policy class for a multi-word scoped active model instance" do
policy = Pundit.policy(user, avatar_four_five_six)
expect(policy.class).to eq ProjectOneTwoThree::AvatarFourFiveSixPolicy
end
it "returns nil if the given policy can't be found" do
expect(Pundit.policy(user, article)).to be_nil
expect(Pundit.policy(user, Article)).to be_nil
end
it "returns the specified NilClassPolicy for nil" do
expect(Pundit.policy(user, nil)).to be_a NilClassPolicy
end
describe "with .policy_class set on the model" do
it "returns an instantiated policy given a plain model instance" do
policy = Pundit.policy(user, artificial_blog)
expect(policy.user).to eq user
expect(policy.blog).to eq artificial_blog
end
it "returns an instantiated policy given a plain model class" do
policy = Pundit.policy(user, ArtificialBlog)
expect(policy.user).to eq user
expect(policy.blog).to eq ArtificialBlog
end
it "returns an instantiated policy given a plain model instance providing an anonymous class" do
policy = Pundit.policy(user, article_tag)
expect(policy.user).to eq user
expect(policy.tag).to eq article_tag
end
it "returns an instantiated policy given a plain model class providing an anonymous class" do
policy = Pundit.policy(user, ArticleTag)
expect(policy.user).to eq user
expect(policy.tag).to eq ArticleTag
end
end
end
describe ".policy!" do
it "returns an instantiated policy given a plain model instance" do
policy = Pundit.policy!(user, post)
expect(policy.user).to eq user
expect(policy.post).to eq post
end
it "returns an instantiated policy given an active model instance" do
policy = Pundit.policy!(user, comment)
expect(policy.user).to eq user
expect(policy.comment).to eq comment
end
it "returns an instantiated policy given a plain model class" do
policy = Pundit.policy!(user, Post)
expect(policy.user).to eq user
expect(policy.post).to eq Post
end
it "returns an instantiated policy given an active model class" do
policy = Pundit.policy!(user, Comment)
expect(policy.user).to eq user
expect(policy.comment).to eq Comment
end
it "returns an instantiated policy given a symbol" do
policy = Pundit.policy!(user, :criteria)
expect(policy.class).to eq CriteriaPolicy
expect(policy.user).to eq user
expect(policy.criteria).to eq :criteria
end
it "returns an instantiated policy given an array of symbols" do
policy = Pundit.policy!(user, %i[project criteria])
expect(policy.class).to eq Project::CriteriaPolicy
expect(policy.user).to eq user
expect(policy.criteria).to eq :criteria
end
it "throws an exception if the given policy can't be found" do
expect { Pundit.policy!(user, article) }.to raise_error(Pundit::NotDefinedError)
expect { Pundit.policy!(user, Article) }.to raise_error(Pundit::NotDefinedError)
end
it "returns the specified NilClassPolicy for nil" do
expect(Pundit.policy!(user, nil)).to be_a NilClassPolicy
end
it "raises an error with a invalid policy constructor" do
expect do
Pundit.policy(user, Wiki)
end.to raise_error(Pundit::InvalidConstructorError, "Invalid # constructor is called")
end
end
describe "#verify_authorized" do
it "does nothing when authorized" do
controller.authorize(post)
controller.verify_authorized
end
it "raises an exception when not authorized" do
expect { controller.verify_authorized }.to raise_error(Pundit::AuthorizationNotPerformedError)
end
end
describe "#verify_policy_scoped" do
it "does nothing when policy_scope is used" do
controller.policy_scope(Post)
controller.verify_policy_scoped
end
it "raises an exception when policy_scope is not used" do
expect { controller.verify_policy_scoped }.to raise_error(Pundit::PolicyScopingNotPerformedError)
end
end
describe "#pundit_policy_authorized?" do
it "is true when authorized" do
controller.authorize(post)
expect(controller.pundit_policy_authorized?).to be true
end
it "is false when not authorized" do
expect(controller.pundit_policy_authorized?).to be false
end
end
describe "#pundit_policy_scoped?" do
it "is true when policy_scope is used" do
controller.policy_scope(Post)
expect(controller.pundit_policy_scoped?).to be true
end
it "is false when policy scope is not used" do
expect(controller.pundit_policy_scoped?).to be false
end
end
describe "#authorize" do
it "infers the policy name and authorizes based on it" do
expect(controller.authorize(post)).to be_truthy
end
it "returns the record on successful authorization" do
expect(controller.authorize(post)).to be(post)
end
it "can be given a different permission to check" do
expect(controller.authorize(post, :show?)).to be_truthy
expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
end
it "can be given a different policy class" do
expect(controller.authorize(post, :create?, policy_class: PublicationPolicy)).to be_truthy
end
it "works with anonymous class policies" do
expect(controller.authorize(article_tag, :show?)).to be_truthy
expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
end
it "throws an exception when the permission check fails" do
expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError)
end
it "throws an exception when a policy cannot be found" do
expect { controller.authorize(Article) }.to raise_error(Pundit::NotDefinedError)
end
it "caches the policy" do
expect(controller.policies[post]).to be_nil
controller.authorize(post)
expect(controller.policies[post]).not_to be_nil
end
it "raises an error when the given record is nil" do
expect { controller.authorize(nil, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
end
it "raises an error with a invalid policy constructor" do
expect { controller.authorize(wiki, :destroy?) }.to raise_error(Pundit::InvalidConstructorError)
end
end
describe "#skip_authorization" do
it "disables authorization verification" do
controller.skip_authorization
expect { controller.verify_authorized }.not_to raise_error
end
end
describe "#skip_policy_scope" do
it "disables policy scope verification" do
controller.skip_policy_scope
expect { controller.verify_policy_scoped }.not_to raise_error
end
end
describe "#pundit_user" do
it "returns the same thing as current_user" do
expect(controller.pundit_user).to eq controller.current_user
end
end
describe "#policy" do
it "returns an instantiated policy" do
policy = controller.policy(post)
expect(policy.user).to eq user
expect(policy.post).to eq post
end
it "throws an exception if the given policy can't be found" do
expect { controller.policy(article) }.to raise_error(Pundit::NotDefinedError)
end
it "raises an error with a invalid policy constructor" do
expect { controller.policy(wiki) }.to raise_error(Pundit::InvalidConstructorError)
end
it "allows policy to be injected" do
new_policy = OpenStruct.new
controller.policies[post] = new_policy
expect(controller.policy(post)).to eq new_policy
end
end
describe "#policy_scope" do
it "returns an instantiated policy scope" do
expect(controller.policy_scope(Post)).to eq :published
end
it "allows policy scope class to be overriden" do
expect(controller.policy_scope(Post, policy_scope_class: PublicationPolicy::Scope)).to eq :published
end
it "throws an exception if the given policy can't be found" do
expect { controller.policy_scope(Article) }.to raise_error(Pundit::NotDefinedError)
end
it "raises an error with a invalid policy scope constructor" do
expect { controller.policy_scope(Wiki) }.to raise_error(Pundit::InvalidConstructorError)
end
it "allows policy_scope to be injected" do
new_scope = OpenStruct.new
controller.policy_scopes[Post] = new_scope
expect(controller.policy_scope(Post)).to eq new_scope
end
end
describe "#permitted_attributes" do
it "checks policy for permitted attributes" do
params = ActionController::Parameters.new(post: {
title: "Hello",
votes: 5,
admin: true
})
action = "update"
expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq(
"title" => "Hello",
"votes" => 5
)
expect(Controller.new(double, action, params).permitted_attributes(post).to_h).to eq("votes" => 5)
end
it "checks policy for permitted attributes for record of a ActiveModel type" do
params = ActionController::Parameters.new(customer_post: {
title: "Hello",
votes: 5,
admin: true
})
action = "update"
expect(Controller.new(user, action, params).permitted_attributes(customer_post).to_h).to eq(
"title" => "Hello",
"votes" => 5
)
expect(Controller.new(double, action, params).permitted_attributes(customer_post).to_h).to eq(
"votes" => 5
)
end
end
describe "#permitted_attributes_for_action" do
it "is checked if it is defined in the policy" do
params = ActionController::Parameters.new(post: {
title: "Hello",
body: "blah",
votes: 5,
admin: true
})
action = "revise"
expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq("body" => "blah")
end
it "can be explicitly set" do
params = ActionController::Parameters.new(post: {
title: "Hello",
body: "blah",
votes: 5,
admin: true
})
action = "update"
expect(Controller.new(user, action, params).permitted_attributes(post, :revise).to_h).to eq("body" => "blah")
end
end
describe "Pundit::NotAuthorizedError" do
it "can be initialized with a string as message" do
error = Pundit::NotAuthorizedError.new("must be logged in")
expect(error.message).to eq "must be logged in"
end
end
end
pundit-2.0.0/spec/spec_helper.rb 0000644 0000041 0000041 00000010430 13337760034 016620 0 ustar www-data www-data require "pundit"
require "pundit/rspec"
require "rack"
require "rack/test"
require "pry"
require "active_support"
require "active_support/core_ext"
require "active_model/naming"
require "action_controller/metal/strong_parameters"
I18n.enforce_available_locales = false
module PunditSpecHelper
extend RSpec::Matchers::DSL
matcher :be_truthy do
match do |actual|
actual
end
end
end
RSpec.configure do |config|
config.include PunditSpecHelper
end
class PostPolicy < Struct.new(:user, :post)
class Scope < Struct.new(:user, :scope)
def resolve
scope.published
end
end
def update?
post.user == user
end
def destroy?
false
end
def show?
true
end
def permitted_attributes
if post.user == user
%i[title votes]
else
[:votes]
end
end
def permitted_attributes_for_revise
[:body]
end
end
class Post < Struct.new(:user)
def self.published
:published
end
def self.read
:read
end
def to_s
"Post"
end
def inspect
"#"
end
end
module Customer
class Post < Post
def model_name
OpenStruct.new(param_key: "customer_post")
end
def self.policy_class
PostPolicy
end
def policy_class
self.class.policy_class
end
end
end
class CommentScope
attr_reader :original_object
def initialize(original_object)
@original_object = original_object
end
def ==(other)
original_object == other.original_object
end
end
class CommentPolicy < Struct.new(:user, :comment)
class Scope < Struct.new(:user, :scope)
def resolve
CommentScope.new(scope)
end
end
end
class PublicationPolicy < Struct.new(:user, :publication)
class Scope < Struct.new(:user, :scope)
def resolve
scope.published
end
end
def create?
true
end
end
class Comment
extend ActiveModel::Naming
end
class CommentsRelation
def initialize(empty = false)
@empty = empty
end
def blank?
@empty
end
def model_name
Comment.model_name
end
end
class Article; end
class BlogPolicy < Struct.new(:user, :blog); end
class Blog; end
class ArtificialBlog < Blog
def self.policy_class
BlogPolicy
end
end
class ArticleTagOtherNamePolicy < Struct.new(:user, :tag)
def show?
true
end
def destroy?
false
end
end
class ArticleTag
def self.policy_class
ArticleTagOtherNamePolicy
end
end
class CriteriaPolicy < Struct.new(:user, :criteria); end
module Project
class CommentPolicy < Struct.new(:user, :comment)
class Scope < Struct.new(:user, :scope)
def resolve
scope
end
end
end
class CriteriaPolicy < Struct.new(:user, :criteria); end
class PostPolicy < Struct.new(:user, :post)
class Scope < Struct.new(:user, :scope)
def resolve
scope.read
end
end
end
end
class DenierPolicy < Struct.new(:user, :record)
def update?
false
end
end
class Controller
include Pundit
# Mark protected methods public so they may be called in test
# rubocop:disable Layout/AccessModifierIndentation, Style/AccessModifierDeclarations
public(*Pundit.protected_instance_methods)
# rubocop:enable Layout/AccessModifierIndentation, Style/AccessModifierDeclarations
attr_reader :current_user, :action_name, :params
def initialize(current_user, action_name, params)
@current_user = current_user
@action_name = action_name
@params = params
end
end
class NilClassPolicy < Struct.new(:user, :record)
class Scope
def initialize(*)
raise Pundit::NotDefinedError, "Cannot scope NilClass"
end
end
def show?
false
end
def destroy?
false
end
end
class Wiki; end
class WikiPolicy
class Scope
# deliberate typo method
def initalize; end
end
end
class PostFourFiveSix < Struct.new(:user); end
class CommentFourFiveSix; extend ActiveModel::Naming; end
module ProjectOneTwoThree
class CommentFourFiveSixPolicy < Struct.new(:user, :post); end
class CriteriaFourFiveSixPolicy < Struct.new(:user, :criteria); end
class PostFourFiveSixPolicy < Struct.new(:user, :post); end
class TagFourFiveSix < Struct.new(:user); end
class TagFourFiveSixPolicy < Struct.new(:user, :tag); end
class AvatarFourFiveSix; extend ActiveModel::Naming; end
class AvatarFourFiveSixPolicy < Struct.new(:user, :avatar); end
end
pundit-2.0.0/CHANGELOG.md 0000644 0000041 0000041 00000005563 13337760034 014674 0 ustar www-data www-data # Pundit
## 2.0.0 (2018-07-21)
No changes since beta1
## 2.0.0.beta1 (2018-07-04)
- Add `policy_class` option to `authorize` to be able to override the policy. (#441)
- Add `policy_scope_class` option to `authorize` to be able to override the policy scope. (#441)
- Fix `param_key` issue when passed an array. (#529)
- Only pass last element of "namespace array" to policy and scope. (#529)
- Allow specification of a `NilClassPolicy`. (#525)
- Make sure `policy_class` override is called when passed an array. (#475)
- Raise `InvalidConstructorError` if a policy or policy scope with an invalid constructor is called. (#462)
- Use `action_name` instead of `params[:action]`. (#419)
- Add `pundit_params_for` method to make it easy to customize params fetching. (#502)
- Return passed object from `#authorize` method to make chaining possible. (#385)
## 1.1.0 (2016-01-14)
- Can retrieve policies via an array of symbols/objects.
- Add autodetection of param key to `permitted_attributes` helper.
- Hide some methods which should not be actions.
- Permitted attributes should be expanded.
- Generator uses `RSpec.describe` according to modern best practices.
## 1.0.1 (2015-05-27)
- Fixed a regression where NotAuthorizedError could not be ininitialized with a string.
- Use `camelize` instead of `classify` for symbol policies to prevent weird pluralizations.
## 1.0.0 (2015-04-19)
- Caches policy scopes and policies.
- Explicitly setting the policy for the controller via `controller.policy = foo` has been removed. Instead use `controller.policies[record] = foo`.
- Explicitly setting the policy scope for the controller via `controller.policy_policy = foo` has been removed. Instead use `controller.policy_scopes[scope] = foo`.
- Add `permitted_attributes` helper to fetch attributes from policy.
- Add `pundit_policy_authorized?` and `pundit_policy_scoped?` methods.
- Instance variables are prefixed to avoid collisions.
- Add `Pundit.authorize` method.
- Add `skip_authorization` and `skip_policy_scope` helpers.
- Better errors when checking multiple permissions in RSpec tests.
- Better errors in case `nil` is passed to `policy` or `policy_scope`.
- Use `inspect` when printing object for better errors.
- Dropped official support for Ruby 1.9.3
## 0.3.0 (2014-08-22)
- Extend the default `ApplicationPolicy` with an `ApplicationPolicy::Scope` (#120)
- Fix RSpec 3 deprecation warnings for built-in matchers (#162)
- Generate blank policy spec/test files for Rspec/MiniTest/Test::Unit in Rails (#138)
## 0.2.3 (2014-04-06)
- Customizable error messages: `#query`, `#record` and `#policy` methods on `Pundit::NotAuthorizedError` (#114)
- Raise a different `Pundit::AuthorizationNotPerformedError` when `authorize` call is expected in controller action but missing (#109)
- Update Rspec matchers for Rspec 3 (#124)
## 0.2.2 (2014-02-07)
- Customize the user to be passed into policies: `pundit_user` (#42)
pundit-2.0.0/.rubocop.yml 0000644 0000041 0000041 00000003113 13337760034 015322 0 ustar www-data www-data AllCops:
DisplayCopNames: true
TargetRubyVersion: 2.1
Exclude:
- "gemfiles/**/*"
- "vendor/**/*"
- "lib/generators/**/*"
Metrics/BlockLength:
Exclude:
- "**/*_spec.rb"
Metrics/MethodLength:
Max: 40
Metrics/ModuleLength:
Max: 200
Exclude:
- "**/*_spec.rb"
Metrics/LineLength:
Max: 120
Metrics/AbcSize:
Enabled: false
Metrics/CyclomaticComplexity:
Enabled: false
Metrics/PerceivedComplexity:
Enabled: false
Style/StructInheritance:
Enabled: false
Layout/AlignParameters:
EnforcedStyle: with_fixed_indentation
Style/StringLiterals:
EnforcedStyle: double_quotes
Style/StringLiteralsInInterpolation:
EnforcedStyle: double_quotes
Layout/ClosingParenthesisIndentation:
Enabled: false
Style/OneLineConditional:
Enabled: false
Style/AndOr:
Enabled: false
Style/Not:
Enabled: false
Documentation:
Enabled: false # TODO: Enable again once we have more docs
Layout/CaseIndentation:
EnforcedStyle: case
SupportedStyles:
- case
- end
IndentOneStep: true
Style/PercentLiteralDelimiters:
PreferredDelimiters:
'%w': "[]"
'%W': "[]"
Layout/AccessModifierIndentation:
EnforcedStyle: outdent
Style/SignalException:
Enabled: false
Layout/IndentationWidth:
Enabled: false
Style/TrivialAccessors:
ExactNameMatch: true
Layout/EndAlignment:
EnforcedStyleAlignWith: variable
Layout/DefEndAlignment:
Enabled: false
Lint/HandleExceptions:
Enabled: false
Style/SpecialGlobalVars:
Enabled: false
Style/TrivialAccessors:
Enabled: false
Layout/IndentHash:
Enabled: false
Style/DoubleNegation:
Enabled: false
pundit-2.0.0/.gitignore 0000644 0000041 0000041 00000000236 13337760034 015043 0 ustar www-data www-data *.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
bin
pundit-2.0.0/CODE_OF_CONDUCT.md 0000644 0000041 0000041 00000002615 13337760034 015655 0 ustar www-data www-data # Contributor Code of Conduct
As contributors and maintainers of this project, we pledge to respect all
people who contribute through reporting issues, posting feature requests,
updating documentation, submitting pull requests or patches, and other
activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual
language or imagery, derogatory comments or personal attacks, trolling, public
or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct. Project maintainers who do not
follow the Code of Conduct may be removed from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by opening an issue or contacting one or more of the project
maintainers.
This Code of Conduct is adapted from the [Contributor
Covenant](http:contributor-covenant.org), version 1.0.0, available at
[http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
pundit-2.0.0/Rakefile 0000644 0000041 0000041 00000000512 13337760034 014515 0 ustar www-data www-data require "rubygems"
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "yard"
require "rubocop/rake_task"
RuboCop::RakeTask.new
desc "Run all examples"
RSpec::Core::RakeTask.new(:spec) do |t|
t.rspec_opts = %w[--color]
end
YARD::Rake::YardocTask.new do |t|
t.files = ["lib/**/*.rb"]
end
task default: :spec
pundit-2.0.0/lib/ 0000755 0000041 0000041 00000000000 13337760034 013620 5 ustar www-data www-data pundit-2.0.0/lib/pundit.rb 0000644 0000041 0000041 00000027130 13337760034 015453 0 ustar www-data www-data # frozen_string_literal: true
require "pundit/version"
require "pundit/policy_finder"
require "active_support/concern"
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/module/introspection"
require "active_support/dependencies/autoload"
# @api public
module Pundit
SUFFIX = "Policy".freeze
# @api private
module Generators; end
# @api private
class Error < StandardError; end
# Error that will be raised when authorization has failed
class NotAuthorizedError < Error
attr_reader :query, :record, :policy
def initialize(options = {})
if options.is_a? String
message = options
else
@query = options[:query]
@record = options[:record]
@policy = options[:policy]
message = options.fetch(:message) { "not allowed to #{query} this #{record.inspect}" }
end
super(message)
end
end
# Error that will be raised if a policy or policy scope constructor is not called correctly.
class InvalidConstructorError < Error; end
# Error that will be raised if a controller action has not called the
# `authorize` or `skip_authorization` methods.
class AuthorizationNotPerformedError < Error; end
# Error that will be raised if a controller action has not called the
# `policy_scope` or `skip_policy_scope` methods.
class PolicyScopingNotPerformedError < AuthorizationNotPerformedError; end
# Error that will be raised if a policy or policy scope is not defined.
class NotDefinedError < Error; end
extend ActiveSupport::Concern
class << self
# Retrieves the policy for the given record, initializing it with the
# record and user and finally throwing an error if the user is not
# authorized to perform the given action.
#
# @param user [Object] the user that initiated the action
# @param record [Object] the object we're checking permissions of
# @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)
# @param policy_class [Class] the policy class we want to force use of
# @raise [NotAuthorizedError] if the given query method returned false
# @return [Object] Always returns the passed object record
def authorize(user, record, query, policy_class: nil)
policy = policy_class ? policy_class.new(user, record) : policy!(user, record)
raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)
record
end
# Retrieves the policy scope for the given record.
#
# @see https://github.com/varvet/pundit#scopes
# @param user [Object] the user that initiated the action
# @param scope [Object] the object we're retrieving the policy scope for
# @raise [InvalidConstructorError] if the policy constructor called incorrectly
# @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
def policy_scope(user, scope)
policy_scope = PolicyFinder.new(scope).scope
policy_scope.new(user, pundit_model(scope)).resolve if policy_scope
rescue ArgumentError
raise InvalidConstructorError, "Invalid #<#{policy_scope}> constructor is called"
end
# Retrieves the policy scope for the given record.
#
# @see https://github.com/varvet/pundit#scopes
# @param user [Object] the user that initiated the action
# @param scope [Object] the object we're retrieving the policy scope for
# @raise [NotDefinedError] if the policy scope cannot be found
# @raise [InvalidConstructorError] if the policy constructor called incorrectly
# @return [Scope{#resolve}] instance of scope class which can resolve to a scope
def policy_scope!(user, scope)
policy_scope = PolicyFinder.new(scope).scope!
policy_scope.new(user, pundit_model(scope)).resolve
rescue ArgumentError
raise InvalidConstructorError, "Invalid #<#{policy_scope}> constructor is called"
end
# Retrieves the policy for the given record.
#
# @see https://github.com/varvet/pundit#policies
# @param user [Object] the user that initiated the action
# @param record [Object] the object we're retrieving the policy for
# @raise [InvalidConstructorError] if the policy constructor called incorrectly
# @return [Object, nil] instance of policy class with query methods
def policy(user, record)
policy = PolicyFinder.new(record).policy
policy.new(user, pundit_model(record)) if policy
rescue ArgumentError
raise InvalidConstructorError, "Invalid #<#{policy}> constructor is called"
end
# Retrieves the policy for the given record.
#
# @see https://github.com/varvet/pundit#policies
# @param user [Object] the user that initiated the action
# @param record [Object] the object we're retrieving the policy for
# @raise [NotDefinedError] if the policy cannot be found
# @raise [InvalidConstructorError] if the policy constructor called incorrectly
# @return [Object] instance of policy class with query methods
def policy!(user, record)
policy = PolicyFinder.new(record).policy!
policy.new(user, pundit_model(record))
rescue ArgumentError
raise InvalidConstructorError, "Invalid #<#{policy}> constructor is called"
end
private
def pundit_model(record)
record.is_a?(Array) ? record.last : record
end
end
# @api private
module Helper
def policy_scope(scope)
pundit_policy_scope(scope)
end
end
included do
helper Helper if respond_to?(:helper)
if respond_to?(:helper_method)
helper_method :policy
helper_method :pundit_policy_scope
helper_method :pundit_user
end
end
protected
# @return [Boolean] whether authorization has been performed, i.e. whether
# one {#authorize} or {#skip_authorization} has been called
def pundit_policy_authorized?
!!@_pundit_policy_authorized
end
# @return [Boolean] whether policy scoping has been performed, i.e. whether
# one {#policy_scope} or {#skip_policy_scope} has been called
def pundit_policy_scoped?
!!@_pundit_policy_scoped
end
# Raises an error if authorization has not been performed, usually used as an
# `after_action` filter to prevent programmer error in forgetting to call
# {#authorize} or {#skip_authorization}.
#
# @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
# @raise [AuthorizationNotPerformedError] if authorization has not been performed
# @return [void]
def verify_authorized
raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
end
# Raises an error if policy scoping has not been performed, usually used as an
# `after_action` filter to prevent programmer error in forgetting to call
# {#policy_scope} or {#skip_policy_scope} in index actions.
#
# @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
# @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
# @return [void]
def verify_policy_scoped
raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
end
# Retrieves the policy for the given record, initializing it with the record
# and current user and finally throwing an error if the user is not
# authorized to perform the given action.
#
# @param record [Object] the object we're checking permissions of
# @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
# If omitted then this defaults to the Rails controller action name.
# @param policy_class [Class] the policy class we want to force use of
# @raise [NotAuthorizedError] if the given query method returned false
# @return [Object] Always returns the passed object record
def authorize(record, query = nil, policy_class: nil)
query ||= "#{action_name}?"
@_pundit_policy_authorized = true
policy = policy_class ? policy_class.new(pundit_user, record) : policy(record)
raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)
record
end
# Allow this action not to perform authorization.
#
# @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
# @return [void]
def skip_authorization
@_pundit_policy_authorized = true
end
# Allow this action not to perform policy scoping.
#
# @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
# @return [void]
def skip_policy_scope
@_pundit_policy_scoped = true
end
# Retrieves the policy scope for the given record.
#
# @see https://github.com/varvet/pundit#scopes
# @param scope [Object] the object we're retrieving the policy scope for
# @param policy_scope_class [Class] the policy scope class we want to force use of
# @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
def policy_scope(scope, policy_scope_class: nil)
@_pundit_policy_scoped = true
policy_scope_class ? policy_scope_class.new(pundit_user, scope).resolve : pundit_policy_scope(scope)
end
# Retrieves the policy for the given record.
#
# @see https://github.com/varvet/pundit#policies
# @param record [Object] the object we're retrieving the policy for
# @return [Object, nil] instance of policy class with query methods
def policy(record)
policies[record] ||= Pundit.policy!(pundit_user, record)
end
# Retrieves a set of permitted attributes from the policy by instantiating
# the policy class for the given record and calling `permitted_attributes` on
# it, or `permitted_attributes_for_{action}` if `action` is defined. It then infers
# what key the record should have in the params hash and retrieves the
# permitted attributes from the params hash under that key.
#
# @see https://github.com/varvet/pundit#strong-parameters
# @param record [Object] the object we're retrieving permitted attributes for
# @param action [Symbol, String] the name of the action being performed on the record (e.g. `:update`).
# If omitted then this defaults to the Rails controller action name.
# @return [Hash{String => Object}] the permitted attributes
def permitted_attributes(record, action = action_name)
policy = policy(record)
method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
"permitted_attributes_for_#{action}"
else
"permitted_attributes"
end
pundit_params_for(record).permit(*policy.public_send(method_name))
end
# Retrieves the params for the given record.
#
# @param record [Object] the object we're retrieving params for
# @return [ActionController::Parameters] the params
def pundit_params_for(record)
params.require(PolicyFinder.new(record).param_key)
end
# Cache of policies. You should not rely on this method.
#
# @api private
# rubocop:disable Naming/MemoizedInstanceVariableName
def policies
@_pundit_policies ||= {}
end
# rubocop:enable Naming/MemoizedInstanceVariableName
# Cache of policy scope. You should not rely on this method.
#
# @api private
# rubocop:disable Naming/MemoizedInstanceVariableName
def policy_scopes
@_pundit_policy_scopes ||= {}
end
# rubocop:enable Naming/MemoizedInstanceVariableName
# Hook method which allows customizing which user is passed to policies and
# scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
#
# @see https://github.com/varvet/pundit#customize-pundit-user
# @return [Object] the user object to be used with pundit
def pundit_user
current_user
end
private
def pundit_policy_scope(scope)
policy_scopes[scope] ||= Pundit.policy_scope!(pundit_user, scope)
end
end
pundit-2.0.0/lib/generators/ 0000755 0000041 0000041 00000000000 13337760034 015771 5 ustar www-data www-data pundit-2.0.0/lib/generators/test_unit/ 0000755 0000041 0000041 00000000000 13337760034 020007 5 ustar www-data www-data pundit-2.0.0/lib/generators/test_unit/templates/ 0000755 0000041 0000041 00000000000 13337760034 022005 5 ustar www-data www-data pundit-2.0.0/lib/generators/test_unit/templates/policy_test.rb 0000644 0000041 0000041 00000000321 13337760034 024664 0 ustar www-data www-data require 'test_helper'
class <%= class_name %>PolicyTest < ActiveSupport::TestCase
def test_scope
end
def test_show
end
def test_create
end
def test_update
end
def test_destroy
end
end
pundit-2.0.0/lib/generators/test_unit/policy_generator.rb 0000644 0000041 0000041 00000000473 13337760034 023705 0 ustar www-data www-data module TestUnit
module Generators
class PolicyGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path('templates', __dir__)
def create_policy_test
template 'policy_test.rb', File.join('test/policies', class_path, "#{file_name}_policy_test.rb")
end
end
end
end
pundit-2.0.0/lib/generators/rspec/ 0000755 0000041 0000041 00000000000 13337760034 017105 5 ustar www-data www-data pundit-2.0.0/lib/generators/rspec/templates/ 0000755 0000041 0000041 00000000000 13337760034 021103 5 ustar www-data www-data pundit-2.0.0/lib/generators/rspec/templates/policy_spec.rb 0000644 0000041 0000041 00000001212 13337760034 023735 0 ustar www-data www-data require '<%= File.exists?('spec/rails_helper.rb') ? 'rails_helper' : 'spec_helper' %>'
RSpec.describe <%= class_name %>Policy do
let(:user) { User.new }
subject { described_class }
permissions ".scope" do
pending "add some examples to (or delete) #{__FILE__}"
end
permissions :show? do
pending "add some examples to (or delete) #{__FILE__}"
end
permissions :create? do
pending "add some examples to (or delete) #{__FILE__}"
end
permissions :update? do
pending "add some examples to (or delete) #{__FILE__}"
end
permissions :destroy? do
pending "add some examples to (or delete) #{__FILE__}"
end
end
pundit-2.0.0/lib/generators/rspec/policy_generator.rb 0000644 0000041 0000041 00000000470 13337760034 023000 0 ustar www-data www-data module Rspec
module Generators
class PolicyGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path('templates', __dir__)
def create_policy_spec
template 'policy_spec.rb', File.join('spec/policies', class_path, "#{file_name}_policy_spec.rb")
end
end
end
end
pundit-2.0.0/lib/generators/pundit/ 0000755 0000041 0000041 00000000000 13337760034 017274 5 ustar www-data www-data pundit-2.0.0/lib/generators/pundit/policy/ 0000755 0000041 0000041 00000000000 13337760034 020573 5 ustar www-data www-data pundit-2.0.0/lib/generators/pundit/policy/templates/ 0000755 0000041 0000041 00000000000 13337760034 022571 5 ustar www-data www-data pundit-2.0.0/lib/generators/pundit/policy/templates/policy.rb 0000644 0000041 0000041 00000000242 13337760034 024413 0 ustar www-data www-data <% module_namespacing do -%>
class <%= class_name %>Policy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
end
<% end -%>
pundit-2.0.0/lib/generators/pundit/policy/policy_generator.rb 0000644 0000041 0000041 00000000511 13337760034 024462 0 ustar www-data www-data module Pundit
module Generators
class PolicyGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path('templates', __dir__)
def create_policy
template 'policy.rb', File.join('app/policies', class_path, "#{file_name}_policy.rb")
end
hook_for :test_framework
end
end
end
pundit-2.0.0/lib/generators/pundit/policy/USAGE 0000644 0000041 0000041 00000000260 13337760034 021360 0 ustar www-data www-data Description:
Generates a policy for a model with the given name.
Example:
rails generate pundit:policy user
This will create:
app/policies/user_policy.rb
pundit-2.0.0/lib/generators/pundit/install/ 0000755 0000041 0000041 00000000000 13337760034 020742 5 ustar www-data www-data pundit-2.0.0/lib/generators/pundit/install/templates/ 0000755 0000041 0000041 00000000000 13337760034 022740 5 ustar www-data www-data pundit-2.0.0/lib/generators/pundit/install/templates/application_policy.rb 0000644 0000041 0000041 00000001010 13337760034 027137 0 ustar www-data www-data 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
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope.all
end
end
end
pundit-2.0.0/lib/generators/pundit/install/install_generator.rb 0000644 0000041 0000041 00000000440 13337760034 025001 0 ustar www-data www-data module Pundit
module Generators
class InstallGenerator < ::Rails::Generators::Base
source_root File.expand_path('templates', __dir__)
def copy_application_policy
template 'application_policy.rb', 'app/policies/application_policy.rb'
end
end
end
end
pundit-2.0.0/lib/generators/pundit/install/USAGE 0000644 0000041 0000041 00000000133 13337760034 021526 0 ustar www-data www-data Description:
Generates an application policy as a starting point for your application.
pundit-2.0.0/lib/pundit/ 0000755 0000041 0000041 00000000000 13337760034 015123 5 ustar www-data www-data pundit-2.0.0/lib/pundit/version.rb 0000644 0000041 0000041 00000000114 13337760034 017131 0 ustar www-data www-data # frozen_string_literal: true
module Pundit
VERSION = "2.0.0".freeze
end
pundit-2.0.0/lib/pundit/policy_finder.rb 0000644 0000041 0000041 00000005507 13337760034 020305 0 ustar www-data www-data module Pundit
# Finds policy and scope classes for given object.
# @api public
# @example
# user = User.find(params[:id])
# finder = PolicyFinder.new(user)
# finder.policy #=> UserPolicy
# finder.scope #=> UserPolicy::Scope
#
class PolicyFinder
attr_reader :object
# @param object [any] the object to find policy and scope classes for
#
def initialize(object)
@object = object
end
# @return [nil, Scope{#resolve}] scope class which can resolve to a scope
# @see https://github.com/varvet/pundit#scopes
# @example
# scope = finder.scope #=> UserPolicy::Scope
# scope.resolve #=> <#ActiveRecord::Relation ...>
#
def scope
"#{policy}::Scope".safe_constantize
end
# @return [nil, Class] policy class with query methods
# @see https://github.com/varvet/pundit#policies
# @example
# policy = finder.policy #=> UserPolicy
# policy.show? #=> true
# policy.update? #=> false
#
def policy
klass = find(object)
klass.is_a?(String) ? klass.safe_constantize : klass
end
# @return [Scope{#resolve}] scope class which can resolve to a scope
# @raise [NotDefinedError] if scope could not be determined
#
def scope!
scope or raise NotDefinedError, "unable to find scope `#{find(object)}::Scope` for `#{object.inspect}`"
end
# @return [Class] policy class with query methods
# @raise [NotDefinedError] if policy could not be determined
#
def policy!
policy or raise NotDefinedError, "unable to find policy `#{find(object)}` for `#{object.inspect}`"
end
# @return [String] the name of the key this object would have in a params hash
#
def param_key
model = object.is_a?(Array) ? object.last : object
if model.respond_to?(:model_name)
model.model_name.param_key.to_s
elsif model.is_a?(Class)
model.to_s.demodulize.underscore
else
model.class.to_s.demodulize.underscore
end
end
private
def find(subject)
if subject.is_a?(Array)
modules = subject.dup
last = modules.pop
context = modules.map { |x| find_class_name(x) }.join("::")
[context, find(last)].join("::")
elsif subject.respond_to?(:policy_class)
subject.policy_class
elsif subject.class.respond_to?(:policy_class)
subject.class.policy_class
else
klass = find_class_name(subject)
"#{klass}#{SUFFIX}"
end
end
def find_class_name(subject)
if subject.respond_to?(:model_name)
subject.model_name
elsif subject.class.respond_to?(:model_name)
subject.class.model_name
elsif subject.is_a?(Class)
subject
elsif subject.is_a?(Symbol)
subject.to_s.camelize
else
subject.class
end
end
end
end
pundit-2.0.0/lib/pundit/rspec.rb 0000644 0000041 0000041 00000005427 13337760034 016574 0 ustar www-data www-data require "active_support/core_ext/array/conversions"
module Pundit
module RSpec
module Matchers
extend ::RSpec::Matchers::DSL
# rubocop:disable Metrics/BlockLength
matcher :permit do |user, record|
match_proc = lambda do |policy|
@violating_permissions = permissions.find_all do |permission|
!policy.new(user, record).public_send(permission)
end
@violating_permissions.empty?
end
match_when_negated_proc = lambda do |policy|
@violating_permissions = permissions.find_all do |permission|
policy.new(user, record).public_send(permission)
end
@violating_permissions.empty?
end
failure_message_proc = lambda do |policy|
was_were = @violating_permissions.count > 1 ? "were" : "was"
"Expected #{policy} to grant #{permissions.to_sentence} on " \
"#{record} but #{@violating_permissions.to_sentence} #{was_were} not granted"
end
failure_message_when_negated_proc = lambda do |policy|
was_were = @violating_permissions.count > 1 ? "were" : "was"
"Expected #{policy} not to grant #{permissions.to_sentence} on " \
"#{record} but #{@violating_permissions.to_sentence} #{was_were} granted"
end
if respond_to?(:match_when_negated)
match(&match_proc)
match_when_negated(&match_when_negated_proc)
failure_message(&failure_message_proc)
failure_message_when_negated(&failure_message_when_negated_proc)
else
match_for_should(&match_proc)
match_for_should_not(&match_when_negated_proc)
failure_message_for_should(&failure_message_proc)
failure_message_for_should_not(&failure_message_when_negated_proc)
end
def permissions
current_example = ::RSpec.respond_to?(:current_example) ? ::RSpec.current_example : example
current_example.metadata[:permissions]
end
end
# rubocop:enable Metrics/BlockLength
end
module DSL
def permissions(*list, &block)
describe(list.to_sentence, permissions: list, caller: caller) { instance_eval(&block) }
end
end
module PolicyExampleGroup
include Pundit::RSpec::Matchers
def self.included(base)
base.metadata[:type] = :policy
base.extend Pundit::RSpec::DSL
super
end
end
end
end
RSpec.configure do |config|
if RSpec::Core::Version::STRING.split(".").first.to_i >= 3
config.include(
Pundit::RSpec::PolicyExampleGroup,
type: :policy,
file_path: %r{spec/policies}
)
else
config.include(
Pundit::RSpec::PolicyExampleGroup,
type: :policy,
example_group: { file_path: %r{spec/policies} }
)
end
end
pundit-2.0.0/CONTRIBUTING.md 0000644 0000041 0000041 00000002242 13337760034 015303 0 ustar www-data www-data ## Security issues
If you have found a security related issue, please do not file an issue on
GitHub or send a PR addressing the issue. Contact
[Jonas](mailto:jonas.nicklas@gmail.com) directly. You will be given public
credit for your disclosure.
## Reporting issues
Please try to answer the following questions in your bug report:
- What did you do?
- What did you expect to happen?
- What happened instead?
Make sure to include as much relevant information as possible. Ruby version,
Pundit version, OS version and any stack traces you have are very valuable.
## Pull Requests
- **Add tests!** Your patch won't be accepted if it doesn't have tests.
- **Document any change in behaviour**. Make sure the README and any other
relevant documentation are kept up-to-date.
- **Create topic branches**. Please don't ask us to pull from your master branch.
- **One pull request per feature**. If you want to do more than one thing, send
multiple pull requests.
- **Send coherent history**. Make sure each individual commit in your pull
request is meaningful. If you had to make multiple intermediate commits while
developing, please squash them before sending them to us.
pundit-2.0.0/.yardopts 0000644 0000041 0000041 00000000062 13337760034 014716 0 ustar www-data www-data --api public --hide-void-return --markup markdown
pundit-2.0.0/Gemfile 0000644 0000041 0000041 00000000327 13337760034 014347 0 ustar www-data www-data source "https://rubygems.org"
ruby RUBY_VERSION
gemspec
group :development, :test do
gem "actionpack"
gem "activemodel"
gem "bundler"
gem "pry"
gem "rake"
gem "rspec"
gem "rubocop"
gem "yard"
end
pundit-2.0.0/pundit.gemspec 0000644 0000041 0000041 00000001473 13337760034 015727 0 ustar www-data www-data lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "pundit/version"
Gem::Specification.new do |gem|
gem.name = "pundit"
gem.version = Pundit::VERSION
gem.authors = ["Jonas Nicklas", "Elabs AB"]
gem.email = ["jonas.nicklas@gmail.com", "dev@elabs.se"]
gem.description = "Object oriented authorization for Rails applications"
gem.summary = "OO authorization for Rails"
gem.homepage = "https://github.com/varvet/pundit"
gem.license = "MIT"
gem.files = `git ls-files`.split($/)
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ["lib"]
gem.add_dependency "activesupport", ">= 3.0.0"
end
pundit-2.0.0/LICENSE.txt 0000644 0000041 0000041 00000002070 13337760034 014674 0 ustar www-data www-data Copyright (c) 2012 Jonas Nicklas, Elabs AB
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.