GithubHelp home page GithubHelp logo

palkan / action_policy-graphql Goto Github PK

View Code? Open in Web Editor NEW
149.0 149.0 9.0 90 KB

Action Policy integration for GraphQL

License: MIT License

Ruby 99.66% Shell 0.34%
authorization graphql hacktoberfest ruby

action_policy-graphql's Introduction

Hey / Привет 👋

My name is Vladimir (or Вова), and I'm a principal backend engineer at @evilmartians.

📕 My book "Layered design for Ruby on Rails applications" is avalable now: Amazon | Packt

I'm working on:

Check out some of my blog posts:

...and conference talks:

action_policy-graphql's People

Contributors

artplan1 avatar bibendi avatar haines avatar palkan avatar rzaharenkov avatar sponomarev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

action_policy-graphql's Issues

authorize! method doesn't work inside mutation

Get error while authorizing inside mutation(think the same will be with resolver).

These steps are done:

class Types::BaseObject < GraphQL::Schema::Object
include ActionPolicy::GraphQL::Behaviour
end

For using authorization helpers in mutations

class Types::BaseMutation < GraphQL::Schema::Mutation
include ActionPolicy::GraphQL::Behaviour
end

For using authorization helpers in resolvers

class Types::BaseResolver < GraphQL::Schema::Resolver
include ActionPolicy::GraphQL::Behaviour
end

Error message:

*** NoMethodError Exception: undefined method `authorize!' for #Mutations::Companies::CreateCompanyMutation:0x00007fb12c8fcd40
Did you mean? authorized?

But when authorize inside mutation_type.rb then it works:
field :create_company, mutation: Mutations::Companies::CreateCompanyMutation, authorize: {to: :create?, with: CompanyPolicy}

Have a look please :)

`expose_authorization_rules` Changes

Hey @palkan hope you're doing well!

This week we just started to implement feature gates in our GraphQL API.

I was wondering if you've done this kind of thing, and if you think ActionPolicy was a good place for this kind of thing.

One thought that I had was if I added feature gates to a policy I could share it across the app vs just in GraphQL.

another question that I had was around the expose_authorization_rules API, do you have any real world examples as to why you would want to show anything other than a boolean here? I find myself only ever reaching for the .value in the AuthorizationResult.

I was curious if you'd have any realistic use cases for the rest of the information like reasons that are exposed in the AuthorizationResult.

If not, I'd propose a change to something like:

 expose_authorization_rules :edit_profile, 
                             :create_profile, 
                             boolean: true

to return a boolean instead of an AuthorizationResult.

what do you think?

Raise AuthorizationContextMissing before resolving

When have authorize: true set in my field (or I use authorize! user, to: :show?), I have found that even with a nil user context, it still tries to 'resolve', which causes a lot of undefined method x for nil:Nilclass in places I have called the user object in the resolver.

I expected that if I have authorized: true set, or authorize! user, to: :show? , then it should first check for the presence of the authorization context before even resolving, and raise AuthorizationContextMissing beforehand.

This way I do not have to check for the user object in every resolver, and I can instead use a

  rescue_from(ActionPolicy::AuthorizationContextMissing) do
    raise GraphQL::ExecutionError, 'Please login to perform this action'
  end

Right now I have to raise GraphQL::ExecutionError, 'Please login to perform this action' unless current_user in every resolver

NoMethodError: undefined method `authorize_mutation_raise_exception'

Tell us about your environment

Ruby Version: 2.6

Framework Version (Rails, whatever): Rails

Action Policy Version: 0.5.3

Action Policy GraphQL Version: 0.5.0

What did you do?

Using field authorization in mutations.

What did you expect to happen?

It should raise authorization error if user is not authorized to access fields.

What actually happened?

It raises NoMethodError:

     NoMethodError:
       undefined method `authorize_mutation_raise_exception' for ActionPolicy::GraphQL:Module
       Did you mean?  authorize_raise_exception
                      preauthorize_mutation_raise_exception

AuthorizedField is included (prepended) multiple time into field class

Tell us about your environment

Ruby Version: 2.6

Framework Version (Rails, whatever): Rails

Action Policy Version: 0.4.4

Action Policy GraphQL Version: 0.4.0

What did you do?

We are trying to combine action_policy-graphql with other custom extensions.

What did you expect to happen?

My custom extension is used for authentication and it is supposed to run before authentication. I have my extension injected inside field class and I expect that base field initialize method runs before initialize method of any module included into it.

What actually happened?

I can see that AuthorizedField is included multiple times and it's initialize method is called before initialize method defined in class:

> self.class.ancestors
=> [ActionPolicy::GraphQL::AuthorizedField,
 Types::BaseField,
 ActionPolicy::GraphQL::AuthorizedField,
 GraphQL::Schema::Member::AcceptsDefinition::InitializeExtension,
 GraphQL::Schema::Member::AcceptsDefinition::ToGraphQLExtension,
 GraphQL::Schema::Field,
 GraphQL::Schema::FindInheritedValue::EmptyObjects,
 GraphQL::Schema::Member::HasPath,
 GraphQL::Schema::Member::HasAstNode,
 GraphQL::Schema::Member::HasArguments::ArgumentObjectLoader,
 GraphQL::Schema::Member::HasArguments,
 GraphQL::Schema::Member::AcceptsDefinition,
 GraphQL::Schema::Member::CachedGraphQLDefinition,
 ActiveSupport::ToJsonWithActiveSupportEncoder,
 Object,
 Nori::CoreExt::Object,
 FriendlyId::ObjectUtils,
 RequireAll,
 PP::ObjectMixin,
 JSON::Ext::Generator::GeneratorMethods::Object,
 ActiveSupport::Tryable,
 ActiveSupport::Dependencies::Loadable,
 Kernel,
 BasicObject]

Figure out mutation/resolver input or arguments authorization

Related to palkan/action_policy#89 and #22.

Let's think about how we can use policies to authorize mutations inputs (and, probably, resolvers in general). What are the possible use-cases? I can recall a few:

  • Making some input fields or arguments non-public (maybe, we can re-use authorize_field: here?), for example, available only to admins/managers/etc.
  • Checking the arguments/input fields values for access rights, e.g., when a user provide an associated object ID as a part of the payload.

The goal of this ticket is to discuss how a general approach could look like, collect examples (and convert them into test cases), design the API.

Unable to override `authorization_namespace`

Tell us about your environment

Ruby Version: 2.7.1

Framework Version (Rails, whatever): Rails 6.1.4.1

Action Policy Version: 0.6.0

Action Policy GraphQL Version: 0.5.3

What did you do?

I'm trying to redefine authorization_namespace but nothing worked. For example for a mutation, the namespace is Mutations. When i tried to override it in BaseMutation, it was still Mutations. So i set a pry inside my definition but it was never reached. I also tried overriding it inside BaseObject or the GraphqlController directly but nothing worked.

What did you expect to happen?

To be able to custom the authorization_namespace

What actually happened?

My definition of authorization_namespace was never acknowledged.

Can you tell me how are we supposed to do this ? Thanks a lot

expose_authorization_rules with custom context

Hi, can I transfer some additional data to expose_authorization_rules and then use them in the policy?
for example:
expose_authorization_rules :create?, context: { test_data: "I'm test_data" }
then get this information in the policy for use

def create?
  self.context[:test_data]
end

Can ruby-next be a development-only dependency like action_policy?

I noticed in the latest update that action_policy-graphql is pulling ruby-next in as a dependency rather than just ruby-next-core.

Would it be possible to set this gem up to ship transpiled files in the gem (like action_policy itself) rather than requiring consumers of the gem to install ruby-next?

Best practice of record list / create permissions exposition and authorization

I like the idea behind the expose_authorization_rules macro. But it only works with existing records. What about if I have to check if there are a permission for the user to create a post? At the moment I'm using the following pattern:

user.rb

module Types
  module Models
    class User < Types::Base::Object
      # ...
      field :can_create_post, Boolean, null: false
      def can_create_post
        allowed_to?(:create_post?, object)
      end
    end
  end
end

user_policy.rb

class UserPolicy < ApplicationPolicy
  # actions with associations
  def create_post?
    policy_for(record: Post).create?
  end
end

post_policy.rb

class PostPolicy < ApplicationPolicy
  def create?
    user.role.is_a?(Manager)
  end
end

post.rb (mutation)

      def resolve(text:)
        authorize!(current_user || ::User, to: :create_post?)

I found it most simple, but seems like it might be improved.

Field extensions should not modify options hash

Hello,

We are on our way to implement pre-authorization on fields in our GraphQL schema and on mutations in particular. We have quite a lot of them and, to reduce redundant declaration, we'd like to use the .with_options method provided by ActiveSupport to merge options common to mutation field declarations around the same model.

Environment

Ruby Version: 3.2
Framework Version: Rails 7.1
Action Policy Version: 0.6.7
Action Policy GraphQL Version: 0.5.3

What did we do?

class Mutation < Types::Objects::Base
  with_options preauthorize: { with: ThingPolicy } do
    field :thing_create, mutation: Mutations::Things::Create
    field :thing_update, mutation: Mutations::Things::Update
  end
end

What did we expect to happen?

with_options should work as expected and actually forward options to every .field call.

What actually happened?

with_options works as expected but field extensions consume options in a destructive way: as they internally use Hash#delete on the options hash, the first field is correctly extended but subsequent fields receive empty options.


This can be fixed in a minimalist way with something like:

# In action_policy/graphql/authorized_field.rb
module ActionPolicy::GraphQL::AuthorizedField
  class Extension < ::GraphQL::Schema::FieldExtension
+   def initialize(field:, options:) = super(field:, options: options&.dup || {})
+
    def extract_option(key, &default)
      value = options.fetch(key, &default)
      options.delete key
      value
    end
  end
end

Note: This will not be fixed in graphql-ruby. See rmosolgo/graphql-ruby#4733.

Graphql context in Policies?

Hi, there is a way to pass Graphql context to the Policies? Example:

# frozen_string_literal: true

module Types
  class QueryType < Types::BaseObject
    field :user,
          resolver: Queries::User::Fetch,
          preauthorize: { to: :user?, with: Queries::UsersPolicy }
  end
end
module Queries
  class UsersPolicy < ApplicationPolicy
    authorize :user, allow_nil: true

    def initialize(user, record)
      # context here
      variables = record.context.query.provided_variables
    end

    def user?
      user.admin?
    end
  end
end

I need the query variables from graphql

preauthorize doesn't default to expected values

e.g.

I would expect preauthorize: { raise: false, with: PostPolicy } to default to preauthorize: { to: :index?, raise: false, with: PostPolicy } but it does not, in the README it states this is the expected behavior. So I guess this is a bug?

[How to] authorize specific arguments in a mutation

Hi there @palkan! 👋

First of all, thanks for your amazing gem :)

Here is my issue: I would like to be able to authorize some specific arguments in a mutation, and not only the mutation as a whole.

Let's say I have a mutation to update a User. Maybe the user itself could update its first_name, but only the admin could upgrade its role, and the manager could assign it in a specific team.

I would like to do something like this:

class Mutations::UpdateUser < BaseMutation
  argument :user_id, ID, loads: Types::UserType, required: true
  argument :first_name, String, required: false # no specific authorization here
  argument :last_name, String, required: false # no specific authorization here
  argument :team_id, ID, loads: Types::Team, required: false, authorize: true
  argument :role, Types::RoleEnum, required: false, authorize: true

  def resolve(user:, **params)
    authorize! user, to: :update?
    user.update!(params)
    { user: user }
  end
end

And then, in my policy class, I could have something like this:

class UserPolicy < ApplicationPolicy
  def team? = user.manager? || user.admin?
  def role? = user.admin?

  def update? # my mutation authorization logic
end

For now, the only way to achieve this I found was to do all this argument-validation logic inside my resolver, but the implementation feels a bit lame.
For example:

def resolve(user:, team: nil, role: nil, **params)
  authorize! user, to: :update?
  authorize! user, to: :update_team? if team
  authorize! user, to: :update_role? if role
end

Is there a better way to manage this?

Thanks a lot :)

no examples of authorized_scope with relay connection type

Hey @palkan,

Hope you're doing well.

I was wondering if you could show an example of how I might implement an authorized_scope with a relay connection.

I tried setting it up initially but it gave me the following error:

Couldn't find policy class for [#<Types::QueryType:0x00007f986eb5a730 @object=nil, @context=#<Query::Context ...>, @__policies_cache__={\"//70146334492460\"=>nil}, @authorization_namespace=Types>, \"Couldn't find implicit authorization target for Types::QueryType. Please, provide policy class explicitly using `with` option or define the `implicit_authorization_target` method.\"] (Array)```

Modules reloading works incorrectly in development

It works as expected in production (with enabled eager loading). But I'm having difficulties in development when I change any file in app/graphql it starts to fail with the following error:

error: "NoMethodError"
message: "undefined method `expose_authorization_rules' for Types::ResourceType:Class"

Tell us about your environment

Ruby Version: 2.6.6

Framework Version (Rails, whatever): 5.2

Action Policy Version: 0.5.1

Action Policy GraphQL Version: 0.5.3

What did you do?

Changing code in app/graphql while running rails server in development mode.

What did you expect to happen?

Method expose_authorization_rules can be used to expose resource authentication rules.

What actually happened?

Getting NoMethodError exception.

Howto setup for graphql ruby 1.9.14

a few questions I have is one,

in the case that my current_user is called viewer in context, what does the modification of that look like to setup ActionPolicy GraphQL?

also, I initially tried this:

# frozen_string_literal: true

class Types::Base < GraphQL::Schema::Object
  include ActionPolicy::GraphQL::Behaviour
  field_class Fields::Base

  def current_user
    context[:viewer]
  end
end

but I am having issues it seems.

when I followed the way to authorize objects at a type level .e.g:

class Types::Post < Types::Base
  #... 

  def self.authorized?(object, context)
    super &&
      object.allowed_to?(
        :show?,
        object,
        context: {user: context[:viewer]}
      )
  end
end

I get an undefined method allowed_to?

Handle record with preauthorize?

Hi, @palkan. There is a way to get record or the ID of the record to handle before to execute an action without "preauthorize"?
I do something like this:

Mutation type:

field :update_user,
       mutation: Mutations::Users::UpdateUserMutation,
       authorize: { to: :update?, with: Mutations::UserPolicy }

Mutation:

class UpdateUserMutation < BaseMutation
  argument :id, String, required: true
  argument :data, Types::Inputs::User::UpdateUser, required: true

  type Types::Data::User

  def resolve(id:, data:)
    UserService.new.save(id, data)
  end

Policy:

module Mutations
  class UserPolicy < ApplicationPolicy
    def update?
      user.admin? || own?
    end

    private

    def own?
      record.id == user.id
    end
  end
end

That way I get "not authorized", but it still performs the service action even if not authorized. If I put "preauthorize" i can't get the "record" to handle.. 🤔

I would need something to handle the record and stop the execution of resolve if it was not authorized. Is it possible?

ActionPolicy does not work well with graphql-ruby dataloaders (not thread/fiber safe)

Tell us about your environment

Ruby Version: ruby 3.2.2

Framework Version (Rails, whatever): rails (7.0.4.3), graphql (2.0.21)

Action Policy Version: (0.6.5)

Action Policy GraphQL Version: (0.5.3)

What did you do?

We started using dataloader in our policies to help reduce the amount of queries.

We pass the graphql context as part of the policy context, which allows us to use our dataloaders in the database.

What did you expect to happen?

We expected everything to keep working as it did, but with less or equal amount of queries.

What actually happened?

We started getting completely wrong results when using expose_authorization_rules.

During our investigation we monkey patched the allowance_to method like this:

def allowance_to(rule, record = :__undef__, **options)
  policy = lookup_authorization_policy(record, **options)

  policy.apply(authorization_rule_for(policy, rule))
  policy.result.tap { |a| puts "policy_result: #{rule} #{a.inspect}" }
end

We got following (sample) output.

policy_result: publish? <PublicGraph::Policies::FolderNodePolicy#publish?: false>
policy_result: move_into? <PublicGraph::Policies::FolderNodePolicy#move_into?: false>
policy_result: create_item? <PublicGraph::Policies::FolderNodePolicy#move_into?: false>
policy_result: create_item_in? <PublicGraph::Policies::FolderNodePolicy#move_into?: false>
policy_result: create_folder_in? <PublicGraph::Policies::FolderNodePolicy#move_into?: false>

As you can see a discrepancy on the rule is exposed. The result that is returned is not that of the rule requested.

In order to understand what is happening it is important to know that resolving of these policies have (somewhere in the stack) a call to dataloader. Dataloader uses fibers to do its "magic", basically stopping execution of functions as long as it can.

This seems to be incompatible with the (default on) ActionPolicy::Behaviours::Memoized and ActionPolicy::Behaviours::ThreadMemoized. When calling the apply method (in allowance_to) the result is stored in the instance. When the thread is yielded (by the fiber scheduler) other threads can replace this result with an other call to apply.

As I see it this implementation can not be considered thread safe, especially in its default configuration.

can_create_object? permission location

Hi @palkan ,

Big fan of your work! I'm trying to set up authorization on my project and while it's clear to place the permissions inline when there is an object (edit, destroy), it's less obvious where to put permissions for index and create, which are at the class level.

One way is to place them inside a Type::PermissionType class.

How would you recommend organizing this? Thank you

More releases or beta branch?

Hey there,

Would it be possible to either push more releases to rubygems or make a separate beta branch for passing commits?

Would be good to be able to help test changes while not having to pull down breaking commits.

Cheers!

how to authorize scope at resolver level

Hey @palkan I'm currently in the process of creating a resolver that takes an argument called type

and depending on the type there are different types of authorization scopes I want to apply, however, I'm not sure how or if there's a way to either access resolver arguments in a policy, or how to apply different behaviors of a policy depending on the resolver arguments.

here's an example:

def feed(type:)
  ArticlePolicy.public if type == "PUBLIC"
  ArticlePolicy.personalized if type == "PERSONALIZED"
end

the intent here is depending on which feed is being queried, I want to limit the kind of articles shown. e.g. if they want to see the public feed, they see all public articles, but if they want to see a personalized feed of articles of all the people they follow, then it'll only show those.

How would you approach something like this with ActionPolicy?

Support resolver

Use the shorter version of ActionPolicy::GraphQL::Behaviour to make it work:

module Types
  module Base
    class Resolver < ::GraphQL::Schema::Resolver
      include ActionPolicy::Behaviour
      include ActionPolicy::Behaviours::ThreadMemoized
      include ActionPolicy::Behaviours::Memoized
      include ActionPolicy::Behaviours::Namespaced

      authorize :user, through: :current_user

      def current_user
        context[:current_user]
      end
    end
  end
end

Would be great if it will be supported out of the box.

Upgrading graphql Gem to 1.12.4 Causes Connection Error

Tell us about your environment

Ruby Version:
3.0

Framework Version (Rails, whatever):
Rails 6.1

Action Policy Version:
0.5.5

Action Policy GraphQL Version:
0.5.2

What did you do?

Upgraded graphql gem from 1.12.3 to 1.12.4

What did you expect to happen?

Connections would continue working

What actually happened?

With this code

    field :rewards,
          RewardType.connection_type,
          'Returns a paginated list of all rewards',
          authorized_scope: true,
          null: false

I get

ActionPolicy::NotFound:
       Couldn't find policy class for [#<Types::QueryType:0x00005577c3abc2b0 @object=nil, @context=#<Query::Context ...>, @__authorization_context={:user=>#<User ancestry: nil, id: 795, email: [FILTERED], created_at: "2021-02-16 17:43:54.930691000 +0000", updated_at: "2021-02-16 17:43:55.234755800 +0000", account_id: 989, first_name: [FILTERED], last_name: [FILTERED], employee_number: [FILTERED], manager_id: nil, hire_date: nil, relocate: nil, short_term_plan: nil, long_term_plan: nil, last_email_sent: nil, status: "active", jti: "c4a742a6-7978-467a-85b3-a2cd0691c111", potential_rating_id: nil, departure_risk_id: nil, succession_time_frame_id: nil, experience_before_next_role: "Retro photo booth hammock waistcoat marfa sriracha...", performance_rating_id: nil, review_frequency: "monthly", enabled: true, role_group_id: nil, uuid: "a7769f4e-8694-44c3-a7bf-38e49cc6180a">}, @__policies_cache__={"//users/795-20210216174355234755/347440"=>nil}, @authorization_namespace=Types>, "Couldn't find implicit authorization target for Types::QueryType. Please, provide policy class explicitly using `with` option or define the `implicit_authorization_target` method."] (Array)
     # /bundle/ruby/3.0.0/gems/action_policy-0.5.5/lib/.rbnext/1995.next/action_policy/behaviours/policy_for.rb:42:in `implicit_authorization_target!'
     # /bundle/ruby/3.0.0/gems/action_policy-0.5.5/lib/.rbnext/1995.next/action_policy/behaviours/scoping.rb:17:in `authorized_scope'
     # /bundle/ruby/3.0.0/gems/action_policy-graphql-0.5.2/lib/action_policy/graphql/authorized_field.rb:120:in `after_resolve'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:769:in `block (2 levels) in with_extensions'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:766:in `each'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:766:in `block in with_extensions'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema.rb:115:in `after_lazy'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:764:in `with_extensions'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:696:in `public_send_field'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:609:in `block in resolve'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema.rb:115:in `after_lazy'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:607:in `resolve'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:272:in `block (4 levels) in evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/tracing.rb:66:in `trace'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:271:in `block (3 levels) in evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/query.rb:364:in `block in with_error_handling'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/errors.rb:45:in `with_error_handling'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/query.rb:363:in `with_error_handling'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:270:in `block (2 levels) in evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:432:in `resolve_with_directives'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:267:in `block in evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:516:in `after_lazy'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:214:in `evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:206:in `block in evaluate_selection'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/member/has_arguments.rb:132:in `block (2 levels) in coerce_arguments'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/dataloader/null_dataloader.rb:16:in `append_job'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/member/has_arguments.rb:111:in `block in coerce_arguments'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/member/has_arguments.rb:110:in `each'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/member/has_arguments.rb:110:in `coerce_arguments'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/arguments_cache.rb:49:in `dataload_for'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:205:in `evaluate_selection'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:142:in `block (2 levels) in evaluate_selections'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/dataloader/null_dataloader.rb:16:in `append_job'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:141:in `block in evaluate_selections'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:140:in `each'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:140:in `evaluate_selections'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:62:in `block in run_eager'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/dataloader/null_dataloader.rb:16:in `append_job'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:61:in `run_eager'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter.rb:77:in `block in evaluate'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/tracing.rb:66:in `trace'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter.rb:76:in `evaluate'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter.rb:49:in `begin_query'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:85:in `begin_query'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:104:in `block (2 levels) in run_as_multiplex'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/dataloader/null_dataloader.rb:16:in `append_job'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:104:in `block in run_as_multiplex'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:103:in `each'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:103:in `each_with_index'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:103:in `run_as_multiplex'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:63:in `block (2 levels) in run_queries'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:208:in `block in instrument_and_analyze'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:29:in `block (2 levels) in apply_instrumenters'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:46:in `block (2 levels) in each_query_call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:41:in `each_query_call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:45:in `block in each_query_call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:44:in `each_query_call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:27:in `block in apply_instrumenters'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:26:in `apply_instrumenters'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:187:in `instrument_and_analyze'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:62:in `block in run_queries'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/tracing.rb:66:in `trace'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:60:in `run_queries'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:50:in `run_all'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema.rb:1690:in `multiplex'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema.rb:1661:in `execute'
     # ./app/controllers/graphql_controller.rb:13:in `execute'
     # /bundle/ruby/3.0.0/gems/actiontext-6.1.2.1/lib/action_text/rendering.rb:20:in `with_renderer'
     # /bundle/ruby/3.0.0/gems/actiontext-6.1.2.1/lib/action_text/engine.rb:55:in `block (4 levels) in <class:Engine>'
     # /bundle/ruby/3.0.0/gems/rack-attack-6.5.0/lib/rack/attack.rb:99:in `call'
     # /bundle/ruby/3.0.0/gems/warden-jwt_auth-0.5.0/lib/warden/jwt_auth/middleware/token_dispatcher.rb:20:in `call'
     # /bundle/ruby/3.0.0/gems/warden-jwt_auth-0.5.0/lib/warden/jwt_auth/middleware/revocation_manager.rb:21:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/builder.rb:244:in `call'
     # /bundle/ruby/3.0.0/gems/warden-jwt_auth-0.5.0/lib/warden/jwt_auth/middleware.rb:23:in `call'
     # /bundle/ruby/3.0.0/gems/rack-attack-6.5.0/lib/rack/attack.rb:113:in `call'
     # /bundle/ruby/3.0.0/gems/warden-1.2.9/lib/warden/manager.rb:36:in `block in call'
     # /bundle/ruby/3.0.0/gems/warden-1.2.9/lib/warden/manager.rb:34:in `catch'
     # /bundle/ruby/3.0.0/gems/warden-1.2.9/lib/warden/manager.rb:34:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/tempfile_reaper.rb:15:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/etag.rb:27:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/conditional_get.rb:40:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/head.rb:12:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:266:in `context'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:260:in `call'
     # /bundle/ruby/3.0.0/gems/lograge-0.11.2/lib/lograge/rails_ext/rack/logger.rb:15:in `call_app'
     # /bundle/ruby/3.0.0/gems/railties-6.1.2.1/lib/rails/rack/logger.rb:26:in `block in call'
     # /bundle/ruby/3.0.0/gems/railties-6.1.2.1/lib/rails/rack/logger.rb:26:in `call'
     # /bundle/ruby/3.0.0/gems/request_store-1.5.0/lib/request_store/middleware.rb:19:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/method_override.rb:24:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/runtime.rb:22:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/sendfile.rb:110:in `call'
     # /bundle/ruby/3.0.0/gems/rack-cors-1.1.1/lib/rack/cors.rb:100:in `call'
     # /bundle/ruby/3.0.0/gems/railties-6.1.2.1/lib/rails/engine.rb:539:in `call'
     # /bundle/ruby/3.0.0/gems/rack-test-1.1.0/lib/rack/mock_session.rb:29:in `request'
     # /bundle/ruby/3.0.0/gems/rack-test-1.1.0/lib/rack/test.rb:266:in `process_request'
     # /bundle/ruby/3.0.0/gems/rack-test-1.1.0/lib/rack/test.rb:119:in `request'
     # ./spec/requests/graphql/queries/rewards_spec.rb:120:in `block (4 levels) in <top (required)>'
     # /bundle/ruby/3.0.0/gems/webmock-3.11.2/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>'

With

field :rewards,
          RewardType.connection_type,
          'Returns a paginated list of all rewards',
          authorized_scope: { with: RewardPolicy },
          null: false

I get

ActionPolicy::UnrecognizedScopeTarget:
       Couldn't infer scope type for GraphQL::Pagination::ActiveRecordRelationConnection instance
     # /bundle/ruby/3.0.0/gems/action_policy-0.5.5/lib/action_policy/policy/scoping.rb:105:in `resolve_scope_type'
     # /bundle/ruby/3.0.0/gems/action_policy-0.5.5/lib/.rbnext/1995.next/action_policy/behaviours/scoping.rb:31:in `authorization_scope_type_for'
     # /bundle/ruby/3.0.0/gems/action_policy-0.5.5/lib/.rbnext/1995.next/action_policy/behaviours/scoping.rb:19:in `authorized_scope'
     # /bundle/ruby/3.0.0/gems/action_policy-graphql-0.5.2/lib/action_policy/graphql/authorized_field.rb:120:in `after_resolve'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:769:in `block (2 levels) in with_extensions'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:766:in `each'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:766:in `block in with_extensions'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema.rb:115:in `after_lazy'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:764:in `with_extensions'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:696:in `public_send_field'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:609:in `block in resolve'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema.rb:115:in `after_lazy'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/field.rb:607:in `resolve'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:272:in `block (4 levels) in evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/tracing.rb:66:in `trace'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:271:in `block (3 levels) in evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/query.rb:364:in `block in with_error_handling'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/errors.rb:45:in `with_error_handling'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/query.rb:363:in `with_error_handling'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:270:in `block (2 levels) in evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:432:in `resolve_with_directives'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:267:in `block in evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:516:in `after_lazy'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:214:in `evaluate_selection_with_args'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:206:in `block in evaluate_selection'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/member/has_arguments.rb:132:in `block (2 levels) in coerce_arguments'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/dataloader/null_dataloader.rb:16:in `append_job'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/member/has_arguments.rb:111:in `block in coerce_arguments'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/member/has_arguments.rb:110:in `each'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema/member/has_arguments.rb:110:in `coerce_arguments'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/arguments_cache.rb:49:in `dataload_for'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:205:in `evaluate_selection'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:142:in `block (2 levels) in evaluate_selections'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/dataloader/null_dataloader.rb:16:in `append_job'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:141:in `block in evaluate_selections'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:140:in `each'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:140:in `evaluate_selections'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:62:in `block in run_eager'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/dataloader/null_dataloader.rb:16:in `append_job'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter/runtime.rb:61:in `run_eager'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter.rb:77:in `block in evaluate'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/tracing.rb:66:in `trace'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter.rb:76:in `evaluate'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/interpreter.rb:49:in `begin_query'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:85:in `begin_query'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:104:in `block (2 levels) in run_as_multiplex'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/dataloader/null_dataloader.rb:16:in `append_job'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:104:in `block in run_as_multiplex'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:103:in `each'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:103:in `each_with_index'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:103:in `run_as_multiplex'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:63:in `block (2 levels) in run_queries'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:208:in `block in instrument_and_analyze'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:29:in `block (2 levels) in apply_instrumenters'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:46:in `block (2 levels) in each_query_call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:41:in `each_query_call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:45:in `block in each_query_call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:44:in `each_query_call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:27:in `block in apply_instrumenters'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/instrumentation.rb:26:in `apply_instrumenters'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:187:in `instrument_and_analyze'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:62:in `block in run_queries'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/tracing.rb:66:in `trace'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:60:in `run_queries'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/execution/multiplex.rb:50:in `run_all'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema.rb:1690:in `multiplex'
     # /bundle/ruby/3.0.0/gems/graphql-1.12.4/lib/graphql/schema.rb:1661:in `execute'
     # ./app/controllers/graphql_controller.rb:13:in `execute'
     # /bundle/ruby/3.0.0/gems/actiontext-6.1.2.1/lib/action_text/rendering.rb:20:in `with_renderer'
     # /bundle/ruby/3.0.0/gems/actiontext-6.1.2.1/lib/action_text/engine.rb:55:in `block (4 levels) in <class:Engine>'
     # /bundle/ruby/3.0.0/gems/rack-attack-6.5.0/lib/rack/attack.rb:99:in `call'
     # /bundle/ruby/3.0.0/gems/warden-jwt_auth-0.5.0/lib/warden/jwt_auth/middleware/token_dispatcher.rb:20:in `call'
     # /bundle/ruby/3.0.0/gems/warden-jwt_auth-0.5.0/lib/warden/jwt_auth/middleware/revocation_manager.rb:21:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/builder.rb:244:in `call'
     # /bundle/ruby/3.0.0/gems/warden-jwt_auth-0.5.0/lib/warden/jwt_auth/middleware.rb:23:in `call'
     # /bundle/ruby/3.0.0/gems/rack-attack-6.5.0/lib/rack/attack.rb:113:in `call'
     # /bundle/ruby/3.0.0/gems/warden-1.2.9/lib/warden/manager.rb:36:in `block in call'
     # /bundle/ruby/3.0.0/gems/warden-1.2.9/lib/warden/manager.rb:34:in `catch'
     # /bundle/ruby/3.0.0/gems/warden-1.2.9/lib/warden/manager.rb:34:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/tempfile_reaper.rb:15:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/etag.rb:27:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/conditional_get.rb:40:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/head.rb:12:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:266:in `context'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:260:in `call'
     # /bundle/ruby/3.0.0/gems/lograge-0.11.2/lib/lograge/rails_ext/rack/logger.rb:15:in `call_app'
     # /bundle/ruby/3.0.0/gems/railties-6.1.2.1/lib/rails/rack/logger.rb:26:in `block in call'
     # /bundle/ruby/3.0.0/gems/railties-6.1.2.1/lib/rails/rack/logger.rb:26:in `call'
     # /bundle/ruby/3.0.0/gems/request_store-1.5.0/lib/request_store/middleware.rb:19:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/method_override.rb:24:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/runtime.rb:22:in `call'
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/sendfile.rb:110:in `call'
     # /bundle/ruby/3.0.0/gems/rack-cors-1.1.1/lib/rack/cors.rb:100:in `call'
     # /bundle/ruby/3.0.0/gems/railties-6.1.2.1/lib/rails/engine.rb:539:in `call'
     # /bundle/ruby/3.0.0/gems/rack-test-1.1.0/lib/rack/mock_session.rb:29:in `request'
     # /bundle/ruby/3.0.0/gems/rack-test-1.1.0/lib/rack/test.rb:266:in `process_request'
     # /bundle/ruby/3.0.0/gems/rack-test-1.1.0/lib/rack/test.rb:119:in `request'
     # ./spec/requests/graphql/queries/rewards_spec.rb:146:in `block (4 levels) in <top (required)>'
     # /bundle/ruby/3.0.0/gems/webmock-3.11.2/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>'

Allow to use proc for authorized_scope

Allow to use proc for authorized_scope. Specifically it might be helpful if you would like to pass some ids via scope_options.

  field :events, EventType.connection_type, null: false, authorized_scope: -> object { {with: CustomEventPolicy, scope_options: {city_id: object.id}} }

Exposed authorization rules can't be accessed without user context

Say I had a CourseType with a way to query it from QueryType with no authorization, through a field called courses. Then the following query works as expected, returning a list of Course ids:

{	  
  courses {
    id
  }
}

The problem is, when context[:current_user] is null, attempting to access any of the exposed rules raises an exception Missing policy authorization context: user.

For instance, lets say that the CourseType has a way to query for a list of LessonType with no authorization, through a field called lessons. Additionally, consider that we expose the ability to create lessons on CourseType with the following: expose_authorization_rules :create?, with: LessonPolicy, field_name: :can_create_lesson.

Now, if we ask the value of canCreateLesson, an exception will be raised:

{	  
  courses {
    id
    canCreateLesson {
      value
    }
  }
}

Here is the graphql response with error backtrace:

{
  "errors": [
    {
      "message": "Missing policy authorization context: user",
      "backtrace": [
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy/policy/authorization.rb:57:in `block in initialize'",
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy/policy/authorization.rb:51:in `each'",
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy/policy/authorization.rb:51:in `initialize'",
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy/rails/policy/instrumentation.rb:16:in `block in initialize'",
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/activesupport-6.0.3/lib/active_support/notifications.rb:182:in `instrument'",
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy/rails/policy/instrumentation.rb:16:in `initialize'",
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy/behaviours/policy_for.rb:13:in `new'",
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy/behaviours/policy_for.rb:13:in `policy_for'",
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy/behaviours/thread_memoized.rb:50:in `block in policy_for'",
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy/behaviours/thread_memoized.rb:57:in `block in __policy_thread_memoize__'",
(more lines that are within the graphql gem)

I believe this is a bug? The expected behavior (I think) is to simply return false values with reasons (maybe something like user? as the reason for I18n integration).

Looking through the action_policy-graphql code, it seems like having user in the context is a requirement? However, when I try to set the user context to Current.user || {} to avoid nil values, then I get a new error altogether:

{
  "errors": [
    {
      "message": "Couldn't find policy class for <LessonPolicy#create?: false (reasons: {:lesson=>[:owner?]}) (ActionPolicy::Base::APR)",
      "backtrace": [
        "/Users/neiljohari/.rvm/gems/ruby-2.7.1/gems/action_policy-0.4.3/lib/action_policy.rb:35:in `lookup'",
(only the lookup seems relevant)
...

Make field extension more flexible

There are some scenarios which are not currently covered:

  • Calling authorize! before resolving field (see #6)
  • Calling both authorized_scope and authorize! on the same field (see #8)

Using authorized_scope on input

@palkan is it possible to use authorized_Scope on an input like you would with params from rail's strong parameters?

I saw that you had this feature in Rails and it started to make me wonder why inputs don't work like strong parameters in the context of GraphQL Ruby.

Issue with action policy 0.6.6: `undefined method 'relation_scope'`

Since action_policy version have been bumped to 0.6.6 I go through the following error: undefined method 'relation_scope'.

Tell us about your environment

Ruby Version:
3.2.2

Framework Version (Rails, whatever):
Rails 7.0

Action Policy Version:
0.6.6

Action Policy GraphQL Version:
0.5.3

What did you do?

I simply updated action_policy from 0.6.5 to 0.6.6

What did you expect to happen?

Smooth update since it was a minor version bump.

What actually happened?

It broke my development environment each time a policy class using relation_scope is called.

initializer to set defaults of query and mutations

In GraphQL is often preferred to return nil if you're unauthorized to view something.

and only raise an error in a mutation.

I'd like to have a feature

that works like something along the lines of

config/initializers/action_policy.rb

ActionPolicy::GraphQL.configure do |config|
  config.defaults.mutation.authorize.raise = true
  config.defaults.mutation.preauthorize.raise = true
  config.defaults.query.authorize.raise = false
  config.defaults.query.preauthorize.raise = false
end

to set the defaults of how ActionPolicy works in GraphQL

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.