action_policy icon indicating copy to clipboard operation
action_policy copied to clipboard

Namespaced policy and controller can't find authorization target for index

Open robbevp opened this issue 4 years ago • 8 comments

Tell us about your environment

Ruby Version: 3.0.1

Framework Version (Rails, whatever): Rails 6.1.3.1

Action Policy Version: 0.5.7

What did you do?

I created a controller and namespaced policy very similar to the example of nested modules in the docs.

My controller (simplified)

class Admin::Client::ReportsController < ApplicationController
  def index
    authorize!
    @reports = Report.all
  end

  def show
    @report = Report.find(params[:id])
    authorize! @report
  end
end

My policy

class Admin::Client::ReportPolicy < ApplicationPolicy
  def index?
    user&.admin?
  end

  def show?
    user&.admin?
  end
end

What did you expect to happen?

When calling either index or show, I expect the corresponding policy to be called.

What actually happened?

When calling show, the policy is found and correctly called. When calling index, i get the following error

ActionPolicy::NotFound (Couldn't find policy class for [#<Admin::Client::ReportsController:0x0000000004a8d0>, "Couldn't find implicit authorization target for Admin::Client::ReportsController. Please, provide policy class explicitly using `with` option or define the `implicit_authorization_target` method."] (Array)):

Any help? Since nested namespaces with an index action are mention explicitly in the docs, I thought that I should need to explicitly provde the authorization target

robbevp avatar Jun 23 '21 09:06 robbevp

I recently upgraded my rails version to 6.1.3.2 and I have started getting similar error:

Couldn't find policy class for #<GraphQL::Pagination::ActiveRecordRelationConnection:0x00007f8b55e74a18 @items=#<ActiveRecord::Relation [#<Brand id: 2, created_at: "2020-02-26 08:46:09.450541000 +1100", updated_at: "2021-02-08 11:19:24.364907000 +1100", name: "Adidas", description: "desc....">]>, @parent=nil, @context=#<Query::Context ...>, @field=#<Types::BaseField Query.brands(...): BrandConnection!>, @first_value=10, @after_value=nil, @last_value=nil, @before_value=nil, @arguments={:first=>10}, @edge_class=GraphQL::Pagination::Connection::Edge, @has_max_page_size_override=true, @max_page_size=100> (GraphQL::Pagination::ActiveRecordRelationConnection)

I'm using action_policy and action_policy-graphql gems.

ingnam avatar Jun 28 '21 05:06 ingnam

Hey @ingnam!

That looks like a different problem: GraphQL::Pagination::ActiveRecordRelationConnection instance is passed as the record, which we don't know how to resolve. Likely, related to some GraphQL-ruby changes or other GraphQL gems.

Could you please open a separate issue in https://github.com/palkan/action_policy-graphql ?

palkan avatar Jul 06 '21 13:07 palkan

@robbevp The problem is that controller_name.classify.safe_constantize returns nil for some reason (though we definitely have the Report class).

What if you add Report as an argument?

authorize! Report

(What surprises me more is that an array with exception message is passed as implicit target 🀔 it should raise an exception (unless #raise is overriden))

palkan avatar Jul 06 '21 14:07 palkan

@robbevp Could you please take a look at the question above?

palkan avatar Aug 27 '21 11:08 palkan

Hi @palkan Sorry for my late response.

If I add the class as an argument, everything works as expected.

robbevp avatar Sep 01 '21 07:09 robbevp

Hm, interesting; could you share the results of the following expression (executed right before authorize!):

  • сontroller_name
  • controller_name.classify
  • controller_name.classify.constantize

palkan avatar Sep 01 '21 17:09 palkan

The constantize seems to be the source of the problem:

сontroller_name: reports
сontroller_name.classify: Report

But constantizing throws uninitialized constant Report since my model is also namespaced under the same namespace as the controller/policy. Looking back, this seems to have been left out when I simplified my code - apologies.

Is there a way to make this work with action_policy (without explictly mentioning the model on authorize!)?

robbevp avatar Sep 01 '21 18:09 robbevp

my model is also namespaced under the same namespace as the controller/policy

Ok, that's the answer.

You can override the #implicit_authorization_target for your controllers to take into account namespacing like this:

def implicit_authorization_target
  controller_path.classify.safe_constantize
end

palkan avatar Sep 02 '21 10:09 palkan

thankyou

niudage avatar Mar 06 '23 22:03 niudage