Authorization framework for Ruby/Rails applications
MIT License
Bot releases are hidden (Show)
Support callable objects as scopes:
class PostPolicy < ApplicationPolicy
relation_scope AuthorizedPosts
end
class AuthorizedPosts
def self.call(policy, relation)
user = policy.user
user.admin? ? relation : relation.where(user: user)
end
end
Drop Ruby 2.6 support.
Prism is now used as a parser for debugging utils.
Published by palkan about 3 years ago
Published by palkan about 4 years ago
This release includes a bunch of minor features and improvements based on the community requests and suggestions.
Result#all_details
to return all collected details in a single hash.See #130 for some example use cases.
Added default:
option to the .lookup
method and default_authorization_policy_class
callback to behaviours.
Add skip_verify_authorized!
to Rails controllers integration.
This method allows you to skip the verify_authorized
callback dynamically.
allowance_to
method to authorization behaviours.This method is similar to allowed_to?
but returns an authorization result object.
deny!
and allow!
methods are now parts of the core API.Now you can call deny!
and allow!
in policy rules to fail- or pass-fast.
BREAKING. Pre-check name is no longer added automatically to failure reasons. You should specify the reason
explicitly: deny!(:my_reason)
.
allowed_to?
/ check?
calls within policies.Published by palkan almost 5 years ago
In addition to allow_nil: true
, we now have an option to skip the context altogether:
class OptionalRolePolicy < ActionPolicy::Base
authorize :role, optional: true
end
policy = OptionalRolePolicy.new
policy.role #=> nil
Now you can use action_policy:install
and action_policy:policy MODEL
Rails generators.
action_policy.init
.Triggered every time a new policy object is initialized.
Now you can write tests like this:
expect { subject }.to be_authorized_to(:show?, an_instance_of(User))
Published by palkan over 5 years ago
Seattle.rb special.
tl;dr scopes; instrumentation; i18n integration; better testing and debugging.
Action Policy provides a universal API for defining different scoping rules: for different kinds of data (e.g. Active Record relations or Action Controller parameters) and different contexts (via the _named scopes).
For example, when you want to scope Active Record collections depending on the current user permissions:
class PostsController < ApplicationController
def index
@posts = authorized_scope(Post.all)
end
end
class PostPolicy < ApplicationPolicy
relation_scope do |relation|
next relation if user.admin?
relation.where(user: user)
end
end
And then in your test you check that the correct scope has been applied:
describe UsersController do
subject { get :index }
it "has authorized scope" do
expect { subject }.to have_authorized_scope(:active_record_relation)
.with(PostPolicy).with_target { |target|
# add additional exceptions for the matched target (`Post.all` in our case)
# if you want to make sure that the scope has been applied to the correct data
}
end
end
📖 Read the docs.
full_message
and arbitrary detailsWhen the i18n
gem is available in your app, the reasons
object implements the #full_messages
and the result
object implements the #message
method, which generates the failure messages using the locales (similarly to ActiveMode::Errors
):
action_policy:
policy:
post:
show?: "You cannot view this post"
class ApplicationController < ActionController::Base
rescue_from ActionPolicy::Unauthorized do |ex|
p ex.result.reasons.full_messages #=> ["You cannot view this post"]
end
end
📖 Read the docs.
You can now add a custom meta data to your failure reasons to provide more context:
class ApplicantPolicy < ApplicationPolicy
def show?
allowed_to?(:show?, object.stage)
end
end
class StagePolicy < ApplicationPolicy
def show?
# Add stage title to the failure reason (if any)
# (could be used by client to show more descriptive message)
details[:title] = record.title
# then perform the checks
user.stages.where(id: record.id).exists?
end
end
📖 Read the docs.
Custom DSL for writing policy specs has been addedL
describe PostPolicy do
let(:user) { build_stubbed :user }
let(:record) { build_stubbed :post, draft: false }
let(:context) { {user: user} }
describe_rule :show? do
succeed "when post is published"
failed "when post is draft" do
before { post.draft = false }
succeed "when user is a manager" do
before { user.role = "manager" }
end
end
end
end
📖 Read the docs.
Added Policy#pp(rule)
method to print the annotated rule source code.
It aims to help debugging complex policy rules:
def edit?
binding.pry
(user.name == "John") && (admin? || access_feed?)
end
pry> pp :edit?
MyPolicy#edit?
↳ (
user.name == "John" #=> false
)
AND
(
admin? #=> false
OR
access_feed? #=> true
)
)
📖 Read the docs and check the PR#63.
📖 Read the docs.
Added #implicit_authorization_target
.
See #35.
Implicit authorization target (defined by implicit_authorization_target
) is used when no target specified for authorize!
call.
For example, for Rails controllers integration it's just controller_name.classify.safe_constantize
.
Added Symbol lookup to the lookup chain.
For instance, lookup will implicitly use AdminPolicy
in a following case:
# admin_controller.rb
class AdminController < ApplicationController
authorize! :admin, to: :update_settings
end
Now it's possible to override implicit authorization context
via context
option:
authorize! target, to: :show?, context: {user: another_user}
authorized_scope User.all, context: {user: another_user}
Published by palkan over 6 years ago