Skip to content

Commit

Permalink
Merge 01666f6 into b7e8c7d
Browse files Browse the repository at this point in the history
  • Loading branch information
romansklenar committed Jan 24, 2014
2 parents b7e8c7d + 01666f6 commit 8071130
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 0 deletions.
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -32,6 +32,7 @@ end
group :test do
gem 'draper'
gem 'cancan'
gem 'pundit'
gem 'capybara', '= 1.1.2'
gem 'simplecov', require: false # Test coverage generator. Go to /coverage/ after running tests
gem 'coveralls', require: false # Test coverage website. Go to https://coveralls.io
Expand Down
12 changes: 12 additions & 0 deletions docs/13-authorization-adapter.md
Expand Up @@ -225,3 +225,15 @@ will use it for authorization:
end

To view more details about the CanCan API, visit [https://github.com/ryanb/cancan](https://github.com/ryanb/cancan).

## Using the Pundit Adapter

Active Admin provides an adapter out of the box also for [Pundit](https://github.com/elabs/pundit).

To use the Pundit adapter, simply update the configuration in the Active Admin
initializer:

config.authorization_adapter = ActiveAdmin::PunditAdapter

You can simply use Pundit the way that you would expect and Active Admin will use it for authorization. Check Pundit's documentation to [set up Pundit in your application](https://github.com/elabs/pundit#installation). If you want to use batch actions just ensure that `destroy_all?` method is defined in your policy class. You can use this [template policy](spec/support/templates/policies/application_policy.rb) in your application instead of default one generated by Pundit's `rails g pundit:install` command.

37 changes: 37 additions & 0 deletions features/authorization_pundit.feature
@@ -0,0 +1,37 @@
Feature: Authorizing Access using Pundit

Background:
Given I am logged in
And 1 post exists
And a configuration of:
"""
require 'pundit'
ActiveAdmin.application.namespace(:admin).authorization_adapter = ActiveAdmin::PunditAdapter
ActiveAdmin.register Post do
end
ActiveAdmin.register_page "No Access" do
end
"""
And I am on the index page for posts

@allow-rescue
Scenario: Attempt to access a resource I am not authorized to see
When I go to the last post's edit page
Then I should see "You are not authorized to perform this action"

Scenario: Viewing the default action items
When I follow "View"
Then I should not see an action item link to "Edit"

@allow-rescue
Scenario: Attempting to visit a Page without authorization
When I go to the admin no access page
Then I should see "You are not authorized to perform this action"

@allow-rescue
Scenario: Viewing a page with authorization
When I go to the admin dashboard page
Then I should see "Dashboard"
1 change: 1 addition & 0 deletions lib/active_admin.rb
Expand Up @@ -127,3 +127,4 @@ def after_load(&block)

# Load gem-specific code only if that gem is being used
require 'active_admin/cancan_adapter' if Gem.loaded_specs['cancan']
require 'active_admin/pundit_adapter' if Gem.loaded_specs['pundit']
42 changes: 42 additions & 0 deletions lib/active_admin/pundit_adapter.rb
@@ -0,0 +1,42 @@
require 'pundit'

module ActiveAdmin

class PunditAdapter < AuthorizationAdapter

def authorized?(action, subject = nil)
policy = retreive_policy(subject)
action = format_action(action, subject)

policy.class.method_defined?(action) && policy.send(action)
end

def scope_collection(collection, action = Auth::READ)
# scoping is appliable only to read/index action
# which means there is no way how to scope other actions
Pundit.policy_scope!(user, collection)
end


def retreive_policy(subject)
case subject
when nil then Pundit.policy!(user, resource)
when Class then Pundit.policy!(user, subject.new)
else Pundit.policy!(user, subject)
end
end

def format_action(action, subject)
# https://github.com/elabs/pundit/blob/master/lib/generators/pundit/install/templates/application_policy.rb
case action
when Auth::CREATE then :create?
when Auth::UPDATE then :update?
when Auth::READ then subject.is_a?(Class) ? :index? : :show?
when Auth::DESTROY then subject.is_a?(Class) ? :destroy_all? : :destroy?
else "#{action}?"
end
end

end

end
3 changes: 3 additions & 0 deletions spec/support/rails_template.rb
Expand Up @@ -71,6 +71,9 @@ def set_id
# Add predefined admin resources
directory File.expand_path('../templates/admin', __FILE__), "app/admin"

# Add predefined policies
directory File.expand_path('../templates/policies', __FILE__), 'app/policies'

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))

generate :'active_admin:install'
Expand Down
@@ -0,0 +1,9 @@
module ActiveAdmin
class CommentPolicy < ApplicationPolicy
class Scope < Struct.new(:user, :scope)
def resolve
scope
end
end
end
end
18 changes: 18 additions & 0 deletions spec/support/templates/policies/active_admin/page_policy.rb
@@ -0,0 +1,18 @@
module ActiveAdmin
class PagePolicy < ApplicationPolicy
class Scope < Struct.new(:user, :scope)
def resolve
scope
end
end

def show?
case record.name
when "Dashboard"
true
else
false
end
end
end
end
11 changes: 11 additions & 0 deletions spec/support/templates/policies/admin_user_policy.rb
@@ -0,0 +1,11 @@
class AdminUserPolicy < ApplicationPolicy
class Scope < Struct.new(:user, :scope)
def resolve
scope
end
end

def destroy?
record != user
end
end
45 changes: 45 additions & 0 deletions spec/support/templates/policies/application_policy.rb
@@ -0,0 +1,45 @@
class ApplicationPolicy
attr_reader :user, :record

def initialize(user, record)
@user = user
@record = record
end

def index?
true
end

def show?
scope.where(:id => record.id).exists?
end

def new?
create?
end

def create?
true
end

def edit?
update?
end

def update?
true
end

def destroy?
true
end

def destroy_all?
true
end


def scope
Pundit.policy_scope!(user, record.class)
end
end
7 changes: 7 additions & 0 deletions spec/support/templates/policies/category_policy.rb
@@ -0,0 +1,7 @@
class CategoryPolicy < ApplicationPolicy
class Scope < Struct.new(:user, :scope)
def resolve
scope
end
end
end
15 changes: 15 additions & 0 deletions spec/support/templates/policies/post_policy.rb
@@ -0,0 +1,15 @@
class PostPolicy < ApplicationPolicy
class Scope < Struct.new(:user, :scope)
def resolve
scope
end
end

def update?
record.author == user
end

def destroy?
update?
end
end
11 changes: 11 additions & 0 deletions spec/support/templates/policies/store_policy.rb
@@ -0,0 +1,11 @@
class StorePolicy < ApplicationPolicy
class Scope < Struct.new(:user, :scope)
def resolve
scope
end
end

def destroy?
false
end
end
11 changes: 11 additions & 0 deletions spec/support/templates/policies/user_policy.rb
@@ -0,0 +1,11 @@
class UserPolicy < ApplicationPolicy
class Scope < Struct.new(:user, :scope)
def resolve
scope
end
end

def destroy_all?
true
end
end
36 changes: 36 additions & 0 deletions spec/unit/pundit_adapter_spec.rb
@@ -0,0 +1,36 @@
require 'spec_helper'

describe ActiveAdmin::PunditAdapter do

describe "full integration" do

let(:application) { ActiveAdmin::Application.new }
let(:namespace) { ActiveAdmin::Namespace.new(application, "Admin") }
let(:resource) { namespace.register(Post) }
let(:auth) { namespace.authorization_adapter.new(resource, double) }

before do
namespace.authorization_adapter = ActiveAdmin::PunditAdapter
end

it "should initialize the ability stored in the namespace configuration" do
expect(auth.authorized?(:read, Post)).to eq true
expect(auth.authorized?(:update, Post)).to eq false
end

it "should scope the collection" do
class RSpec::Mocks::MockPolicy < ApplicationPolicy
class Scope < Struct.new(:user, :scope)
def resolve
scope
end
end
end

collection = double
auth.scope_collection(collection, :read)
expect(collection).to eq collection
end
end

end

0 comments on commit 8071130

Please sign in to comment.