-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add authorization features and a Pundit mixin
- Loading branch information
1 parent
736266d
commit 9cf018a
Showing
12 changed files
with
294 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
module Administrate | ||
module Punditize | ||
extend ActiveSupport::Concern | ||
include Pundit | ||
|
||
included do | ||
def scoped_resource | ||
policy_scope_admin super | ||
end | ||
|
||
def authorize_resource(resource) | ||
authorize resource | ||
end | ||
|
||
def show_action?(action, resource) | ||
Pundit.policy!(pundit_user, resource).send("#{action}?".to_sym) | ||
end | ||
|
||
end | ||
|
||
private | ||
|
||
# Like the policy_scope method in stock Pundit, but allows the 'resolve' | ||
# to be overridden by 'resolve_admin' for a different index scope in Admin | ||
# controllers. | ||
def policy_scope_admin(scope) | ||
ps = Pundit::PolicyFinder.new(scope).scope!.new(pundit_user, scope) | ||
if ps.respond_to? :resolve_admin | ||
ps.resolve_admin | ||
else | ||
ps.resolve | ||
end | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# Authorization | ||
|
||
The default configuration of Administrate is "authenticate-only" - once a | ||
user is authenticated, that user has access to every action of every object. | ||
|
||
You can add more fine-grained authorization by overriding methods in the | ||
controller. | ||
|
||
## Using Pundit | ||
|
||
If your app already uses [Pundit](https://github.com/elabs/pundit) for | ||
authorization, you just need to add one line to your | ||
`Admin::ApplicationController`: | ||
|
||
```ruby | ||
include Administrate::Punditize | ||
``` | ||
|
||
This will use all the policies from your main app to determine if the | ||
current user is able to view a given record or perform a given action. | ||
|
||
### Further limiting scope | ||
|
||
You may want to limit the scope for a given user beyond what they | ||
technically have access to see in the main app. For example, a user may | ||
have all public records in their scope, but you want to only show *their* | ||
records in the admin interface to reduce confusion. | ||
|
||
In this case, you can add an additional `resolve_admin` to your policy's | ||
scope and Administrate will use this instead of the `resolve` method. | ||
|
||
For example: | ||
|
||
```ruby | ||
class PostPolicy < ApplicationPolicy | ||
class Scope < Scope | ||
def resolve | ||
scope.all | ||
end | ||
|
||
def resolve_admin | ||
scope.where(owner: user) | ||
end | ||
end | ||
end | ||
``` | ||
|
||
## Authorization without Pundit | ||
|
||
If you use a different authorization library, or you want to roll your own, | ||
you just need to override a few methods in your controllers or | ||
`Admin::ApplicationController`. For example: | ||
|
||
```ruby | ||
# Limit the scope of the given resource | ||
def scoped_resource | ||
super.where(user: current_user) | ||
end | ||
|
||
# Raise an exception if the user is not permitted to access this resource | ||
def authorize_resource(resource) | ||
raise "Erg!" unless show_action?(params[:action], resource) | ||
end | ||
|
||
# Hide links to actions if the user is not allowed to do them | ||
def show_action?(action, resource) | ||
current_user.can? action, resource | ||
end | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
require "rails_helper" | ||
|
||
# Test Authorization by using the Pundit concern and an example policy, | ||
# which will test all the authorization functionality. | ||
|
||
describe Admin::OrdersController, type: :controller do | ||
context "with Punditize concern" do | ||
|
||
controller(Admin::OrdersController) do | ||
include Administrate::Punditize | ||
def pundit_user | ||
Customer.first # assume the user is the first Customer | ||
end | ||
end | ||
|
||
let!(:user) { create :customer } | ||
|
||
before(:each) do | ||
# Create a few orders for the user and a few for other customers | ||
create_list :order, 4, customer: create(:customer) | ||
create_list :order, 7, customer: user | ||
create_list :order, 2, customer: create(:customer) | ||
create_list :order, 2, customer: user | ||
end | ||
|
||
# Policies are defined in order_policy.rb | ||
describe 'GET index' do | ||
it 'shows only the records in the admin scope' do | ||
locals = capture_view_locals { get :index } | ||
expect(locals[:resources].count).to eq(9) # only my orders | ||
end | ||
end | ||
describe 'GET new' do | ||
it 'raises a Pundit error' do | ||
expect{get :new}.to raise_error(Pundit::NotAuthorizedError) | ||
end | ||
end | ||
describe 'GET edit' do | ||
it 'allows me to edit records in Arizona' do | ||
az = create :order, customer: user, address_state: 'AZ' | ||
expect{get :edit, id: az.id}.not_to raise_error | ||
end | ||
it 'does not allow me to edit other records' do | ||
ga = create :order, customer: user, address_state: 'GA' | ||
expect{get :edit, id: ga.id}.to raise_error(Pundit::NotAuthorizedError) | ||
end | ||
end | ||
describe 'DELETE destroy' do | ||
it 'never allows me to delete a record' do | ||
o = create :order, customer: user, address_state: 'AZ' | ||
expect{delete :destroy, id: o.id}.to raise_error(Pundit::NotAuthorizedError) | ||
end | ||
end | ||
describe '#show_action?' do | ||
it 'shows edit actions for records in AZ' do | ||
o = create :order, customer: user, address_state: 'AZ' | ||
expect(controller.show_action? :edit, o).to be true | ||
end | ||
it 'does not show edit actions for records elsewhere' do | ||
o = create :order, customer: user, address_state: 'GA' | ||
expect(controller.show_action? :edit, o).to be false | ||
end | ||
it 'never shows destroy actions' do | ||
o = create :order, customer: user, address_state: 'AZ' | ||
expect(controller.show_action? :destroy, o).to be false | ||
end | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
class ApplicationPolicy | ||
attr_reader :user, :record | ||
|
||
def initialize(user, record) | ||
@user = user | ||
@record = record | ||
end | ||
|
||
def index? | ||
false | ||
end | ||
|
||
def show? | ||
scope.where(:id => record.id).exists? | ||
end | ||
|
||
def create? | ||
false | ||
end | ||
|
||
def new? | ||
create? | ||
end | ||
|
||
def update? | ||
false | ||
end | ||
|
||
def edit? | ||
update? | ||
end | ||
|
||
def destroy? | ||
false | ||
end | ||
|
||
def scope | ||
Pundit.policy_scope!(user, record.class) | ||
end | ||
|
||
class Scope | ||
attr_reader :user, :scope | ||
|
||
def initialize(user, scope) | ||
@user = user | ||
@scope = scope | ||
end | ||
|
||
def resolve | ||
scope | ||
end | ||
end | ||
end |
Oops, something went wrong.