exaspark / graphql-guard Goto Github PK
View Code? Open in Web Editor NEWSimple authorization gem for GraphQL :lock:
License: MIT License
Simple authorization gem for GraphQL :lock:
License: MIT License
Was on 1.2.2, graphql 1.8.10 and just upgraded to 2.0 and graphql 1.11.5 and see that:
rescue_from GraphQL::Guard::NotAuthorizedError do |e|
# some handling
end
Is not hit when said error is raised.
At first I thought it may be due to rmosolgo/graphql-ruby#2140 but looking at the stacktrace I think it may be due to the addition use GraphQL::Execution::Interpreter
:
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:13:in `block in <class:Guard>'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:73:in `ensure_guarded'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:40:in `trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `block in call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/platform_tracing.rb:52:in `block in trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/new_relic_tracing.rb:34:in `block in platform_trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/newrelic_rpm-6.3.0.355/lib/new_relic/agent/method_tracer_helpers.rb:30:in `trace_execution_scoped'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/new_relic_tracing.rb:33:in `platform_trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/platform_tracing.rb:51:in `trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:67:in `trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter/runtime.rb:225:in `block (4 levels) in evaluate_selections'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/query.rb:353:in `block in with_error_handling'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/errors.rb:30:in `with_error_handling'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/query.rb:352:in `with_error_handling'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter/runtime.rb:224:in `block (3 levels) in evaluate_selections'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter/runtime.rb:385:in `resolve_with_directives'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter/runtime.rb:221:in `block (2 levels) in evaluate_selections'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter/runtime.rb:460:in `after_lazy'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter/runtime.rb:179:in `block in evaluate_selections'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter/runtime.rb:126:in `each'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter/runtime.rb:126:in `evaluate_selections'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter/runtime.rb:58:in `run_eager'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter.rb:73:in `block in evaluate'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:81:in `call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `block in call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:42:in `trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `block in call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/platform_tracing.rb:26:in `block in trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/new_relic_tracing.rb:34:in `block in platform_trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/newrelic_rpm-6.3.0.355/lib/new_relic/agent/method_tracer_helpers.rb:30:in `trace_execution_scoped'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/new_relic_tracing.rb:33:in `platform_trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/platform_tracing.rb:25:in `trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:67:in `trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter.rb:72:in `evaluate'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/interpreter.rb:45:in `begin_query'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:112:in `begin_query'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:83:in `block in run_as_multiplex'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:82:in `map'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:82:in `run_as_multiplex'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:61:in `block (2 levels) in run_queries'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:195:in `block in instrument_and_analyze'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/instrumentation.rb:29:in `block (2 levels) in apply_instrumenters'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/instrumentation.rb:46:in `block (2 levels) in each_query_call_hooks'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/instrumentation.rb:41:in `each_query_call_hooks'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/instrumentation.rb:45:in `block in each_query_call_hooks'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/instrumentation.rb:44:in `each_query_call_hooks'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/instrumentation.rb:27:in `block in apply_instrumenters'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/instrumentation.rb:26:in `apply_instrumenters'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:174:in `instrument_and_analyze'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:60:in `block in run_queries'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:81:in `call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `block in call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:42:in `trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `block in call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/platform_tracing.rb:26:in `block in trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/new_relic_tracing.rb:34:in `block in platform_trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/newrelic_rpm-6.3.0.355/lib/new_relic/agent/method_tracer_helpers.rb:30:in `trace_execution_scoped'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/new_relic_tracing.rb:33:in `platform_trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing/platform_tracing.rb:25:in `trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:83:in `call_tracers'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/tracing.rb:67:in `trace'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:58:in `run_queries'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/execution/multiplex.rb:48:in `run_all'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/schema.rb:1656:in `multiplex'
# /Users/sshaw/.rvm/gems/ruby-2.5.7/gems/graphql-1.11.5/lib/graphql/schema.rb:1627:in `execute'
<snip app trace>
When handling the errors on field which is defined in an Interface, field.owner
returns the interface module, instead of the real owner (the subclass of GraphQL::Schema::Object). Probably it should be changed to trace_data[:owner]
?
def ensure_guarded(trace_data)
field = trace_data[:field]
guard_proc = find_guard_proc(field.owner, field)
return yield unless guard_proc
if guard_proc.call(trace_data[:object], args(trace_data), trace_data[:query].context)
yield
else
not_authorized.call(field.owner.graphql_definition, field.name.to_sym)
end
end
Right now we configure this library this way:
use GraphQL::Guard.new(
not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") }
)
And we add gaurds on each of the types:
Types::LocationType = GraphQL::ObjectType.define do
name 'Location'
guard ->(location, _args, context) { context[:current_ability].can?(:read, location) }
end
(We use cancancan
as our ability library)
However this query...
{
location(id:12) {
id
users {name}
}
}
Has a semi-odd behavior:
{
"data": {
"location": {
"id": null,
"users": null
}
},
"errors": [
{
"message": "Not authorized to access Location.id",
"locations": [
{
"line": 3,
"column": 5
}
],
"path": [
"location",
"id"
]
},
{
"message": "Not authorized to access Location.users",
"locations": [
{
"line": 4,
"column": 5
}
],
"path": [
"location",
"users"
]
}
]
}
Rather than:
{
"data": {
"location": null
},
"errors": [
{
"message": "Not authorized to access Location id=12",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"location"
]
}
]
}
Is there an easy way to do that with this library?
we managed to resolve it by doing:
field :location, Types::LocationType do
argument :id, !types.ID
description 'Find a Location by ID'
resolve(lambda do |_obj, args, context|
location = Location.find_by(id: args['id'])
if location.present? &&
context[:current_ability].cannot?(:read, location)
return GraphQL::ExecutionError.new("Not authorized to access Location id=#{args['id']}")
end
location
end)
end
However we have to do that for every field we want to protect. ๐คทโโ๏ธ
I am having issues with mutations where I return a type that is guarded. Let's say for example I have a mutation where a user can reset the password with a token it got via a mail and I return a UserType.
mutation($reset_password_token: String, $password: String) {
newPassword(
reset_password_token: $reset_password_token,
password: $password,
password_confirmation: $password
) { id }
}
The problem is that the UserType is guarded but within the mutation I want to skip the UserType guard policy. Is this possible?
I have my abilities defined in CanCanCan on a field level basis and I'd like to authorize the fields and objects inside a general guard block. I have a BaseObject
from which every GraphQL object inherits:
class BaseObject < GraphQL::Schema::Object
guard ->(obj, _args, context) do
context.fetch(:current_ability).can?(:read, obj.object)
end
end
But I'm missing the field name here, I'd like to do something like this:
class BaseObject < GraphQL::Schema::Object
guard ->(obj, _args, ctx, field) do
ctx.fetch(:current_ability).can?(:read, obj.object, field)
end
end
Am I missing something? Defining the guard block at every field would lead to a lot of duplication and very noisy code:
field :name, String do
guard ->(obj, args, ctx) { ctx.fetch(:current_ability).can?(:read, obj.object, :name) }
end
(of course one could define some helpers to reduce the noise, but it would still be "define the guard at every field" by hand).
I also had a look at your Policy Objects (see https://github.com/exAspArk/graphql-guard#policy-object) but I didn't have any luck with them (it seems to me that the .guard
method of the policy object is only called once and not on every call, but I'm not sure).
So, is there a way to get the field inside the guard block to lazy evaluate if the field is authorized or not?
Hi! I am trying to setup field-level and type-level permissioning, and I'm running to an issue where the type guard seems to have a higher priority than the field. Here is my code:
module Types
ExampleType = GraphQL::ObjectType.define do
name "Example"
description "Example Description"
implements GraphQL::Relay::Node.interface
guard ->(obj, _args, context) { false }
global_id_field :id
field :weight, types.String
field :pulse, types.String
field :height do
type types.String
guard ->(obj, args, context) { true }
resolve lambda {|obj, _args, _context|
obj.height
}
end
end
end
I'm not able to see any of the fields, including height, even though the guard returns true for height.
It seems like arguments of mutations that use loads
to automatically load the types from the database don't get loaded unless they are nested in another type. With queries I have not experienced any issues.
rails: 6.0.3
graphql_ruby: 1.11.6
graphql_guard: 2.0
This query works fine. The facility entity is present in args[facility]
in the GuardPolicy. args
is a hash.
field :facility, Types::Facility::FacilityType, 'Returns a certain Facility',
null: false, authenticate: false do
argument :facility_id, GraphQL::Types::ID, required: true, loads: Types::Facility::FacilityType
argument :locale, String, required: true
end
def facility(facility:, locale:)
facility
end
This mutation does not work. There is a args[exhibitor]
key ('_id' is stripped) but it contains the bare exhibitor_id
. In comparison to the query, args
is of type class.
class UpdateExhibitor < Mutations::BaseMutation
null false
argument :locale, String, required: true
argument :exhibitor_id, ID, required: true, loads: Types::Exhibitor::ExhibitorType
argument :data, Types::Exhibitor::ExhibitorUpdateInput, required: true
type Types::Exhibitor::ExhibitorType
def resolve(locale:, exhibitor:, data:)
EntityInteraction.update_entity(exhibitor, data)
end
end
This mutation does work. It contains the loaded argument in a 'nested' type. The exhibition
entity is available in args[:data][:exhibition]
:
class CreateEvent < Mutations::BaseMutation
null false
argument :locale, String, required: true
argument :data, Types::Shared::AssociatedWithExhibitionCreateInput, required: true
type Types::Event::EventType
def resolve(locale:, data:)
EntityInteraction.create_entity(::Event, data)
end
end
class AssociatedWithExhibitionCreateInput < BaseCreateInput
description 'Attributes for create action of entities associated with exhibition'
argument :exhibition_id, ID, required: true, loads: Exhibition::ExhibitionType
end
I have created a spec file as below:
require 'graphql/guard/testing'
RSpec.describe "GraphQL Guard" do
it 'Authorization for rentals API' do
rentals = Types::QueryType.field_with_guard('rentals', GraphqlPolicy)
result = rentals.guard(obj, args, ctx)
expect(result).to eq(true)
end
end
When I try to run rspec
, I got an error as followings:
NameError:
undefined local variable or method `obj' for #<RSpec::ExampleGroups::GraphQLGuard:0x0000559aa47de350>
Please let me know what I'm wrong?
Was on 1.2.2, graphql 1.8.10 and just upgraded to 2.0 and graphql 1.11.5 and see that previously:
field :something_amaaaaazing, mutation: Mutations::Foo do
guard -> (_, args, ctx) do
# args[someThang] NOTE: camelCase
end
end
to make that work now one must change someThang
to some_thang
.
At first, thank you very much for the great gem, it saved us a lot of headaches! I just came across that the new (experimental) interpreter of graphql-ruby
1.9 does not work with graphql-guard
(probably because it doesn't call lambdas/procs anymore?). Is there a plan to support the new interpreter in the future?
Versions
graphql 1.11.2
graphql-guard 2.0.0
Example Code
module Types
class PostType < Types::BaseObject
field :id, ID, null: false
field :name, String, null: true
field :masked_or_guarded_field, String, null: true, mask: -> (ctx) { !ctx[:current_user].is_admin?} # or, guard: -> (obj, args, ctx) { !ctx[:current_user].is_admin?}
end
end
Expected Response
{
"id": "1",
"name": "This is a name",
"masked_or_guarded_field": null, // or just not there at all
}
Actual Response
null
I've also tried it using this syntax with the same results:
field :masked_or_guarded_field, String, null: true do
mask: -> (ctx) { !ctx[:current_user].is_admin?} # or, guard: -> (obj, args, ctx) { !ctx[:current_user].is_admin?}
end
Let me know if I can provide any more information!
Hello,
Deprecation warnings starting graphql-1.13.1:
Legacy `.to_graphql` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.to_graphql` to use a class-based definition instead.
Called on #<GraphQL::Schema::Field Query.posts(...): [Post!]!> from:
graphql-guard/lib/graphql/guard/testing.rb:29:in `field_with_guard'
.....
From changelog
.to_graphql and .graphql_definition are deprecated and will be removed in GraphQL-Ruby 2.0.
All features using those legacy definitions are already removed
and all behaviors should have been ported to class-based definitions.
So, you should be able to remove those calls entirely.
Please open an issue if you have trouble with it! #3750 #3765
Also I noticed, that inline guards return Array wrapped guard Proc and not just proc right away. Is this intended outcome in graphql or some undesired behaviour - I don't know, should investigate more.
If I don't use this:
def self.guard(type, field)
type.introspection? || RULES.dig(type, field)
end
I can get introspection types without problems which is not good:
Started POST "/graphql" for 172.18.0.1 at 2017-11-26 12:07:58 +0000
Processing by GraphqlController#execute as */*
Parameters: {"query"=>"\n query IntrospectionQuery {\n __schema {\n queryType { name }\n mutationType { name }\n subscriptionType { name }\n types {\n ...FullType\n }\n directives {\n name\n description\n locations\n args {\n ...InputValue\n }\n }\n }\n }\n\n fragment FullType on __Type {\n kind\n name\n description\n fields(includeDeprecated: true) {\n name\n description\n args {\n ...InputValue\n }\n type {\n ...TypeRef\n }\n isDeprecated\n deprecationReason\n }\n inputFields {\n ...InputValue\n }\n interfaces {\n ...TypeRef\n }\n enumValues(includeDeprecated: true) {\n name\n description\n isDeprecated\n deprecationReason\n }\n possibleTypes {\n ...TypeRef\n }\n }\n\n fragment InputValue on __InputValue {\n name\n description\n type { ...TypeRef }\n defaultValue\n }\n\n fragment TypeRef on __Type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n
kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n
kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n }\n }\n }\n", "graphql"=>{"query"=>"\n query IntrospectionQuery {\n __schema {\n queryType { name }\n mutationType { name }\n subscriptionType { name }\n types {\n ...FullType\n }\n directives {\n name\n description\n locations\n args {\n ...InputValue\n }\n }\n }\n }\n\n fragment FullType on __Type {\n kind\n name\n description\n fields(includeDeprecated: true) {\n name\n
description\n args {\n ...InputValue\n }\n type {\n ...TypeRef\n }\n isDeprecated\n deprecationReason\n }\n inputFields {\n ...InputValue\n }\n interfaces {\n
...TypeRef\n }\n enumValues(includeDeprecated: true) {\n name\n description\n isDeprecated\n deprecationReason\n }\n possibleTypes {\n ...TypeRef\n }\n }\n\n fragment InputValue on __InputValue {\n name\n description\n type { ...TypeRef }\n defaultValue\n }\n\n fragment TypeRef on __Type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n
ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n }\n }\n }\n"}}
[active_model_serializers] Rendered ActiveModel::Serializer::Null with GraphQL::Query::Result (12.33ms)
Completed 200 OK in 61ms (Views: 13.7ms | ActiveRecord: 0.0ms)
My GraphQLPolicy:
class GraphqlPolicy
RULES = {
Types::QueryType => {
'*': ->(obj, args, ctx) { ctx[:current_user] } #nil
}
}
def self.guard(type, field)
RULES.dig(type, field)
end
end
Why is introspection skipping authorization?
graphql
gem v1.10 adds GraphQL::Execution::Interpreter
by default in newly generated schemas, and that somehow sidesteps graphql-guard
. For example, with following configuration:
class MySchema < GraphQL::Schema
query(Types::QueryType)
# Opt in to the new runtime (default in future graphql-ruby versions)
use(GraphQL::Execution::Interpreter)
use(GraphQL::Analysis::AST)
use(GraphQL::Guard.new)
end
module Types
class QueryType < Types::BaseObject
guard ->(obj, args, ctx) { false }
field :current_user, UserType, null: false
end
end
the query
query {
currentUser {
id
}
}
is successful. After commenting out
use(GraphQL::Execution::Interpreter)
use(GraphQL::Analysis::AST)
block, the same query returns an error, as expected.
Looking at the docs and other issues I don't think I've seen an exact question like mine, so here goes:
Is there a way to hide a field from introspection if the user is unauthorized to see it? I'd like to have a schema that mixes public and internal fields, and hides the internal fields completely from external users of the application. Is this possible?
undefined method `target' for AppSchema:Class
/.rvm/gems/ruby-2.6.5/gems/graphql-guard-1.3.0/lib/graphql/guard.rb:22:in `use'
/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema.rb:863:in `use'
https://github.com/rmosolgo/graphql-ruby/blob/master/CHANGELOG.md
class ShopType < Types::BaseObject
field :title, String, null: false
field :user, Types::Models::UserType, null: false
end
How would you include a guard on this field for the ShopType, we would like to limit the field user to only signed in users, but allow the other fields on a shop to be public without the need for authentication.
Yes, breaking change but I was thinking with NotAuthorizedError
as a superclass one gets proper error response formatting for free, as GraphQL Ruby will insert the exception's message into the "errors"
property of the response.
And related, how do you feel about changing the default message of NotAuthorizedError
from Query.queryName
to Not authorized to access: Query.queryName
(or similar)?
I just started using this and saw 500 errors with a log message of "Query.queryName"
. Not a helpful message.
Hi!
Thanks for your awesome's work on this gem!
I've followed your CanCanCan documentation but it didn't work until i've added this configuration:
# app/graph/graphql_schema.rb
GraphqlSchema = GraphQL::Schema.define do
query QueryType
mutation MutationType
use GraphQL::Guard.new
resolve_type -> (obj, args, ctx) {
type_name = obj.class.name
Schema.types[type_name]
}
end
I use graphql-guard (~> 1.0.0)
on rails 5.1
Did i missed something?
As i see in: https://github.com/exAspArk/graphql-guard/blob/master/lib/graphql/guard.rb#L6-L7
It looks like there is no mentions of mutations.
Is it an oversight or it needs a lot of work ?
I am using a graphql policy like so:
Types::UserType => {
email: {
guard: ->(obj, args, ctx) { puts obj.inspect; UserPolicy.new(ctx[:current_user], obj).show_protected_fields? }
},
...
This works great. If I use the graphql endpoint and request a user:
{
user(id: 1){
name
email
}
}
It works fine, and the output in the console is :
#<Types::UserType:0x00007f03c20e5750 @object=#<User member_id: 1, email: "[email protected]" ...
However, when I run the same query in rspec, the guard lambda is seemingly receiving the same object (the structure is the same, not the contents).
#<Types::UserType:0x000055a2afb7d6b0 @object=#<User member_id: 10288
However, all my tests fail with:
undefined method `member_id' for #<Types::UserType:0x0000562c884e4f20>
# ./app/policies/user_policy.rb:10:in `show_protected_fields?'
# ./app/graphql/graphql_policy.rb:10:in `block in <class:GraphqlPolicy>'
# /usr/local/bundle/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:70:in `ensure_guarded'
# /usr/local/bundle/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:40:in `trace'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:84:in `call_tracers'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:68:in `trace'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:225:in `block (4 levels) in evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/query.rb:354:in `block in with_error_handling'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/errors.rb:30:in `with_error_handling'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/query.rb:353:in `with_error_handling'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:224:in `block (3 levels) in evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:382:in `resolve_with_directives'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:221:in `block (2 levels) in evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:457:in `after_lazy'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:177:in `block in evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:124:in `each'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:124:in `evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:334:in `block in continue_field'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:457:in `after_lazy'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:329:in `continue_field'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:375:in `continue_field'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:238:in `block (4 levels) in evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:457:in `after_lazy'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:232:in `block (3 levels) in evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:382:in `resolve_with_directives'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:221:in `block (2 levels) in evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:457:in `after_lazy'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:177:in `block in evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:124:in `each'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:124:in `evaluate_selections'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter/runtime.rb:60:in `run_eager'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter.rb:73:in `block in evaluate'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:82:in `call_tracers'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:84:in `block in call_tracers'
# /usr/local/bundle/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:42:in `trace'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:84:in `call_tracers'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:68:in `trace'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter.rb:72:in `evaluate'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/interpreter.rb:45:in `begin_query'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:113:in `begin_query'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:84:in `block in run_as_multiplex'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:83:in `map'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:83:in `run_as_multiplex'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:62:in `block (2 levels) in run_queries'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:196:in `block in instrument_and_analyze'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/instrumentation.rb:29:in `block (2 levels) in apply_instrumenters'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/instrumentation.rb:46:in `block (2 levels) in each_query_call_hooks'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/instrumentation.rb:41:in `each_query_call_hooks'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/instrumentation.rb:45:in `block in each_query_call_hooks'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/instrumentation.rb:44:in `each_query_call_hooks'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/instrumentation.rb:27:in `block in apply_instrumenters'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/instrumentation.rb:26:in `apply_instrumenters'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:175:in `instrument_and_analyze'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:61:in `block in run_queries'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:82:in `call_tracers'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:84:in `block in call_tracers'
# /usr/local/bundle/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:42:in `trace'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:84:in `call_tracers'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/tracing.rb:68:in `trace'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:59:in `run_queries'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/execution/multiplex.rb:49:in `run_all'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/schema.rb:1633:in `multiplex'
# /usr/local/bundle/gems/graphql-1.10.8/lib/graphql/schema.rb:1604:in `execute'
I am using the latest version of graphql, graphql-guard and pundit. My UserPolicy for reference:
def show?
true
end
def show_protected_fields?
puts record.to_json
!user.nil? && (user.admin? || user.member_id == record.member_id)
end
end```
I'd like to mask certain arguments so they are hidden in the schema. I'm currently playing around with this
# in an initializer
GraphQL::Argument.accepts_definitions(mask: GraphQL::Define.assign_metadata_key(:mask))
GraphQL::Schema::Argument.accepts_definition(:mask)
and then use it like this
argument :foo, String, required: false, mask: ->(ctx) { false }
Is this the right way to do it? And can someone explain why I need to define the mask on GraphQL::Argument
AND GraphQL::Schema::Argument
?
Hi,
I just found this gem, and am looking to integrate it. I've run into an issue where my guard lambdas are receiving the graphql type object as the object argument, rather than the found instance that I expect (and the examples in the README also imply that this is what I should be receiving.) This behavior is showing up in all cases.
I'm currently using version 2.0.0, with the latest versions of all the related GraphQL gems.
My setup for testing this is currently
PROJECT_MEMBER_GUARD = lambda { |object, _args, context|
user = context.fetch(:current_user, nil)
ProjectMemberPolicy.new(user, object).show?
}
as the guard, which is attached via
field :project,
Types::ProjectType,
null: false,
description: 'Get a single project',
guard: PROJECT_MEMBER_GUARD do
argument :id, ID, required: true, description: 'The project ID'
end
def project(id:)
# Project.find id also fails
RecordLoader.for(Project).load(id)
end
Am I doing something wrong? Without guard / auth checks, the queries run as expected. I'm completely baffled right now.
The specific error i'm receiving is "undefined method `to_global_id' for #<Types::QueryType:0x000056063dace2e0>"
, but the issue is that the object shouldn't be a Types::QueryType object, as far as my understanding goes.
I'd like to be able to restrict certain enum input values to only super_admin users. Is there a way to do something like this? (This doesn't work obviously)
Types::OrganizationTypeEnumType = GraphQL::EnumType.define do
name 'OrganizationTypeEnumType'
description 'Enum for the possible organization types'
value('PROVIDER', 'provider', value: 'provider'), guard: ->(obj, _args, ctx) { ctx[:current_user].super_admin? }
value('CUSTOMER', 'customer', value: 'customer')
value('THIRD_PARTY', 'third_party', value: 'third_party')
end
graphql-ruby 1.8 is introducing a new class-based syntax, however it seems to break compatibility with graphql-guard.
class Types::Query < GraphQL::Schema::Object
field :posts, [Types::Post], null: false do
guard ->(obj, args, ctx) { true }
end
end
results in undefined method 'guard' for GraphQL::Schema::Field
Is there a potential workaround (aside from just not using the new syntax)?
This is NOT a bug and I immediately apologize for this issue but I'm newbie in Ruby, Rails and this amazing gem.
When I get this: GraphQL::Guard::NotAuthorizedError
:
{
"status": 500,
"error": "Internal Server Error",
"exception": "#<GraphQL::Guard::NotAuthorizedError: Query.users>",
"traces": {
"Application Trace": [
{
"id": 35,
"trace": "app/controllers/graphql_controller.rb:11:in `execute'"
}
],
"Framework Trace": [
{
"id": 0,
"trace": "graphql-guard (1.0.0) lib/graphql/guard.rb:12:in `block in <class:Guard>'"
},
{
"id": 1,
"trace": "graphql-guard (1.0.0) lib/graphql/guard.rb:38:in `block in instrument'"
},
{
"id": 2,
"trace": "graphql (1.7.6) lib/graphql/field.rb:230:in `resolve'"
},
{
"id": 3,
"trace": "graphql (1.7.6) lib/graphql/execution/execute.rb:254:in `call'"
},
{
"id": 4,
"trace": "graphql (1.7.6) lib/graphql/schema/middleware_chain.rb:45:in `invoke_core'"
},
{
"id": 5,
"trace": "graphql (1.7.6) lib/graphql/schema/middleware_chain.rb:38:in `invoke'"
}, ....................
I would prefer something like REST API Unauthorized error 401.
How to do that?
I have to rescue that exception?
Something like:
render json: ["Error 401. Unauthorized."], status: :unauthorized
I am trying to use guard inside the Mutation, using Resolvers class:
class Resolvers::UpdateExample < GraphQL::Function
argument :id, !types.ID
argument :example, !Inputs::ExampleInput
type Types::ExampleType
guard ->(obj, args, ctx) {
true
}
def call(obj,args,ctx)
puts "hello world"
end
end
When I try to call this mutation I get error:
Failure/Error:
guard ->(obj, args, ctx) {
..
NoMethodError:
undefined method `guard' for Resolvers::UpdateExample:Class
Hi,
Can you add guard
to Resolver
Thanks for your open source contributions!
QueryType = GraphQL::ObjectType.define do
name 'Root'
field :widget do
type WidgetType
argument :id, !types.ID
# Can't guard here because we haven't queried for the object yet
resolve ->(_obj, args, _ctx) do
Widget.find(args['id'])
end
end
end
WidgetType = GraphQL::ObjectType.define do
name 'Widget'
guard ->(widget, _args, ctx) do
ctx[:current_user].can?(:read, widget)
end
field :id, type: types.String # => Not authorized to access Widget.id
field :title, TruncatedStringField.new # Returns the title because we haven't explicitly guarded on this path
end
How would one go about guarding the entire WidgetType
and every field under it? It seems to me that TruncatedStringField
should not be returning, but that is in fact what happens. The errors return for every field that's queried, which also seems non-ideal.
Related to the documentation on the testing of the guard proc: https://github.com/exAspArk/graphql-guard#testing
The documentation suggests that I should be able to do QueryType.field_with_guard('posts')
, but when I try to do it with my own Mutations::Root
object (which is a GraphQL::ObjectType
), I get a no method error for field_with_guard
:
Mutations::Root.field_with_guard('some_existing_mutation_endpoint')
NoMethodError: undefined method `field_with_guard' for Mutation:GraphQL::ObjectType
It seems to be the same for all other object types I defined in my GraphQL schema using graphql-ruby
v1.8.4 and graphql-guard
1.0.0 & 1.2.0 (tested with both).
Is there any technical limitation for not supporting the DSL on mutations ?
Something like this:
class CreateDevice < GraphQL::Schema::Mutation
guard -> (_, _, _) { raise StandardError.new }
argument :arg1, String, null: false
def resolve(arg1:)
# My logic
end
end
Hello,
Would you be open to making it configurable for users of graphql-guard to indicate that introspection fields should not be checked for authentication?
We've worked around this by using a policy_object, and checking for type.introspection?
in the guard
method of the policy_object itself, but it does feel like this should be offered out of the box.
If yes, I would even suggest it should be the default option, so that introspection fields would by default be "guard free", and if someone wants to hide their schema they would explicitly have to flip that option (but perhaps that's just me).
Happy to submit a PR if you think this is a sensible idea.
Cheers,
Vlad
Hey, first of all, thanks for making this! Its easy integration with Pundit and its good documentation are pretty great.
Running into an issue here - I'm looking to provide authorization and not_authorized
handling on the field level. So, I copy-pasted your example, swapped out constants ... and found that the procs in the GraphqlPolicy
weren't being executed.
This may be a misunderstanding of graphql-ruby
itself.
Here are snippets:
the schema (verbatim from documentation, except for Types::
prefix):
use GraphQL::Guard.new(
policy_object: Types::GraphqlPolicy,
not_authorized: ->(type, field) {
handler = Types::GraphqlPolicy.not_authorized_handler(type, field)
handler.call(type, field)
}
)
graphql/types/graphql_policy.rb
(named and placed that way to put it in the same load path as UserType etc, definitely open to suggestions) (pretty much copy-pasted)
module Types
class GraphqlPolicy
RULES = {
UserType => {
'*': {
guard: ->(obj, args, ctx) { byebug; nil; UserPolicy.new(ctx[:current_user], obj.object).show? },
},
summary: {
guard: ->(obj, args, ctx) { byebug; nil; UserPolicy.new(ctx[:current_user], obj.object).show_progress? },
not_authorized: ->(type, field) { nil } # simply return nil if not authorized, no errors
}
}
}
def self.guard(type, field)
byebug # This is the only byebug that is evaluated, and produces the output in next snippet
RULES.dig(type, field, :guard)
end
def self.not_authorized_handler(type, field)
Rails.logger.error("Calling not_authorized_handler for #{type.class}: #{type}, #{field}")
RULES.dig(type, field, :not_authorized) || RULES.dig(type, :'*', :not_authorized)
end
end
The output (one of many) from that byebug statement, showing it's not matching the rule:
(byebug)
1: type = User
2: field = :*
3: RULES.dig(type, field, :guard) = nil
The output from the logger:
Calling not_authorized_handler for GraphQL::ObjectType: User, summary
Attempted workarounds:
UserType
to User
-> No change
UserType
to GraphQL::ObjectType::User
-> .../rails-root/app/graphql/types/graphql_policy.rb:4: warning: toplevel constant User referenced by GraphQL::ObjectType::User
RULES.dig("#{type.to_s}Type", field, :not_authorized) || RULES.dig("#{type.to_s}Type", :'*', :not_authorized)
, and using a string as the key in RULES -> it works, but is hacky and adds unnecessary overhead in string conversions
So, the problem seems to be that the parameters sent to guard
don't line up with that object's shape.
What's weird, to me, is that the type is User
, which isn't the graphql type name at all:
module Types
class UserType < Types::BaseObject
User
is the model's class name.
The entire point of using the PORO was to be able to deny access only to that field, returning nil (but still returning an error, ideally, and definitely errors for other fields in other queries). Unfortunately, the best I can do with this is block the whole query:
{
"data": {
"user": null
},
"errors": [
{
"message": "Not authorized to access summary on this User.",
"locations": [
{
"line": 8,
"column": 5
}
],
"path": [
"user",
"summary"
]
}
]
}
IMO it would intuitive if we could just define not_authorized right there in the type definition:
field :summary, UserSummaryType, null: false do
not_authorized ->(type, field) { nil }
guard ->(obj, args, ctx) { UserPolicy.new(ctx[:current_user], obj.object).show_progress? }
end
but unfortunately
undefined method `not_authorized' for #<GraphQL::Schema::Field:>
PS, in the example for not_authorized_handler
, there's a good chance of that returning nil, which throws a NoMethodError
on call()
. Shouldn't there also be a fallback to a default handler in the schema?
not_authorized: ->(type, field) {
handler = Types::GraphqlPolicy.not_authorized_handler(type, field)
handler ? handler.call(type, field) : GraphQL::ExecutionError.new("Not authorized to access #{field} on this #{type}.")
}
I'd like to use mutation type and query type to be used in GraphQL Policy, but what i got, only "QueryType" that always got called even call mutation. Is there an idea why this happen?
Rails 5.2.3
Ruby 2.6.3
class PhoneBookSchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
use GraphQL::Guard.new(
policy_object: GraphqlPolicy,
not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{field}") }
)
end
class GraphqlPolicy
RULES = {
Types::QueryType => {
:user => ->(obj,args,ctx) { ctx[:current_user].is_admin || ctx[:current_ability].can?(:read,User.find(args[:id])) },
:users => ->(obj,args,ctx) { ctx[:current_user].is_admin },
},
Types::MutationType => {
:create_user => ->(obj,args,ctx) { ctx[:current_user].is_admin },
:update_user => ->(obj,args,ctx) { ctx[:current_ability].can?(:update, User.find(args[:user_id])) || ctx[:current_user].is_admin }
},
}
def self.guard(type, field)
RULES.dig(type.metadata[:type_class], field)
end
end
If using guard ->(obj, args, ctx) { some_method }
inside a base type class, and then inheriting from that class you cannot specify overrides per sub class or even per field. It will always use the guard from the parent class.
class ParentType < BaseType
guard ->(obj, args, ctx) { false }
end
class ChildType < ParentType
guard ->(obj, args, ctx) { true } # will not work
field :id, ID, required: true, guard ->(obj, args, ctx) { true } # also will not work
end
This is a question as well as a suggestion.
I'm combining graphql-ruby and graphql-guard with Doorkeeper+Sorcery to handle my authentication. In my graphql_controller.rb
I have:
def current_user
token = Doorkeeper.authenticate(request)
unless token&.accessible?
raise UnauthenticatedError
end
current_resource_owner
rescue UnauthenticatedError => e
GraphQL::ExecutionError.new('Unauthenticated', extensions: { code: 'AUTHENTICATION_ERROR' })
end
And my policy is:
...
Types::WorkoutType => {
'*': ->(obj, args, ctx) { obj.try(:author) == ctx[:current_user] || ctx[:current_user].try(:admin) }
},
...
So when a user is not authenticated, I expect the Unauthenticated
error to be returned, but instead I get the Not authorized to access #{type}.#{field}
defined in graphl_controller.rb
.
Question:
Would it make sense to add the context to the callback so one could do something like this?
use GraphQL::Guard.new(
policy_object: GraphqlPolicy,
not_authorized: ->(type, field, ctx) do
ctx.add_error(GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}"))
end
)
That way we wouldn't remove any other errors that are in there and we can see that we are in fact unauthenticated as well as unauthorized.
First of all, thanks for this great gem! I love the simplicity.
By default this gem follows a blacklisting strategy where you disable access to queries, fields, etc. I'd like it the other way around and whitelist everything that is allowed to be used.
So far I have an implementation that works (based on other discussions on this gem).
The problem with my implementation is that for each field I will need to have a proc which tests the access.
See: https://gist.github.com/yourivdlans/7d1093e5500820804a7ca8d263c98ecf
This might grow to something unmanageable which I'd like to avoid.
What could work is if the field name would be passed into the proc, but I'm not sure if this is the right approach.
Would love to hear your opinion and thoughts :)
Thanks!
I'm getting the Not authorized to access: Xxxxx.id (GraphQL::Guard::NotAuthorizedError)
error in my logs, but are often not sure which record it's talking about, and it's making it a real pain to debug issues.
It usually happens when a GraphQL query with multiple levels of nested fields is executed, and the error is something deeper in the structure, so it can't easily be worked out, by just looking at the arguments.
What I would like to do, is override the default error message so that it prints something like:
raise NotAuthorizedError.new("Not authorized to access: #{type}.#{field} for #{type}.id = #{trace_data[:object].try(:id)}")
and then I'll better understand which record it's referring to when I see this error.
Looking at the source code, I think that it's not currently possible since the 'object' is not passed to the proc. But just putting this here as I think it would be really useful to be able to do.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.