Neil
Neil

Reputation: 5178

Pundit: authorize actions within namespaced controllers

I have a Blog model which has different states. In order to keep a skinny controller and follow the convention of only having CRUD operations per controller, I followed DHH's namespacing controllers pattern and namespaced out the Blog controller.

Now I have a Blogs::NewDraft controller, a Blogs::AwaitingApproval controller, and a Blogs::Active controller.

The issue is with writing my policies to authorize the actions within these namespaced controllers. All the actions in all the namespaced controllers are all authorizing the same Blog model object. The issue is that I need each of the namespaced controllers to authorize in a matching namespaced policy (as opposed to all of the namespaced controllers authorizing within the same blog_policy.rb file.)

Basic Example: For a restful resource with a restful controller that is NOT namespaced you do it something like this:

#app/controllers/blogs_controller.rb
class BlogsController < ApplicationController
  def index
    authorize :blog
    @blogs = Blog.all
  end

  def show
    @blog = Blog.find(1)
    authorize @blog
  end
end

And now the matching Policy

#app/policies/blogs_policy.rb
class BlogPolicy < ApplicationPolicy
  def index?
    user.admin?
  end

  def show?
    record.author == current_user
  end
end

You do it like that when you don't namespace.


Current Code to Try to get Namespacing to work with Pundit: I am namespacing. I am still authorizing a Blog object, but I need to authorize the actions within each namespaced controller within a namespaced policy:

#app/controllers/blogs/new_drafts.rb
class Blogs::NewDraftsController < ApplicationController
  def index
    # doesn't work
    authorize Blog::NewDrafts
    @blogs = Blog.new_drafts
  end
  def show
    @blog = Blog.find(1)
    #doesn't work either
    authorize @blog, Blog::NewDraft
  end
end

So I want that namespaced controller to NOT route to app/policies/blog_policy.rb, but instead to app/policies/blogs/new_draft_policy.rb

#app/policies/blogs/new_draft_policy.rb
class Blogs::NewDraftPolicy < ApplicationPolicy
  def index?
    user.admin?
  end

  def show?
    # the record is a blog from the Blog Model
    record.author == current_user
  end
end

Pundit Documentation and Usage

Upvotes: 1

Views: 1018

Answers (1)

Neil
Neil

Reputation: 5178

Don't know how to route to namespaced policy AND pass in the Blog record. However: below is how you do it when your namespaced policy is able to authorize only based on the current user's permissions/roles:

#app/controllers/blogs/new_drafts.rb
class Blogs::NewDraftsController < ApplicationController
  def index
    authorize [:blogs, :new_draft]
    @blogs = Blog.new_drafts
  end
end

#app/policies/blogs/new_draft_policy.rb
class Blogs::NewDraftPolicy < ApplicationPolicy
  def index?
    user.admin?
  end
end

Upvotes: 1

Related Questions