diff --git a/lib/graphql/execution/interpreter/runtime.rb b/lib/graphql/execution/interpreter/runtime.rb index 805d6c6f0a..d7661e77f8 100644 --- a/lib/graphql/execution/interpreter/runtime.rb +++ b/lib/graphql/execution/interpreter/runtime.rb @@ -50,7 +50,7 @@ def run_eager root_type = schema.root_type_for_operation(root_op_type) path = [] set_all_interpreter_context(query.root_value, nil, nil, path) - object_proxy = authorized_new(root_type, query.root_value, context, path) + object_proxy = authorized_new(root_type, query.root_value, context) object_proxy = schema.sync_lazy(object_proxy) if object_proxy.nil? # Root .authorized? returned false. @@ -193,7 +193,7 @@ def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_co object = owner_object if is_introspection - object = authorized_new(field_defn.owner, object, context, next_path) + object = authorized_new(field_defn.owner, object, context) end total_args_count = field_defn.arguments.size @@ -378,7 +378,7 @@ def continue_field(path, value, owner_type, field, current_type, ast_node, next_ end when "OBJECT" object_proxy = begin - authorized_new(current_type, value, context, path) + authorized_new(current_type, value, context) rescue GraphQL::ExecutionError => err err end @@ -641,22 +641,8 @@ def resolve_type(type, value, path) end end - def authorized_new(type, value, context, path) - trace_payload = { context: context, type: type, object: value, path: path } - - auth_val = context.query.trace("authorized", trace_payload) do - type.authorized_new(value, context) - end - - if context.schema.lazy?(auth_val) - GraphQL::Execution::Lazy.new do - context.query.trace("authorized_lazy", trace_payload) do - context.schema.sync_lazy(auth_val) - end - end - else - auth_val - end + def authorized_new(type, value, context) + type.authorized_new(value, context) end end end diff --git a/lib/graphql/schema/object.rb b/lib/graphql/schema/object.rb index 1de5f78235..1689f036ef 100644 --- a/lib/graphql/schema/object.rb +++ b/lib/graphql/schema/object.rb @@ -48,12 +48,26 @@ class << self # @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy] # @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false` def authorized_new(object, context) - auth_val = context.query.with_error_handling do - begin - authorized?(object, context) - rescue GraphQL::UnauthorizedError => err - context.schema.unauthorized_object(err) + trace_payload = { context: context, type: self, object: object, path: context[:current_path] } + + maybe_lazy_auth_val = context.query.trace("authorized", trace_payload) do + context.query.with_error_handling do + begin + authorized?(object, context) + rescue GraphQL::UnauthorizedError => err + context.schema.unauthorized_object(err) + end + end + end + + auth_val = if context.schema.lazy?(maybe_lazy_auth_val) + GraphQL::Execution::Lazy.new do + context.query.trace("authorized_lazy", trace_payload) do + context.schema.sync_lazy(maybe_lazy_auth_val) + end end + else + maybe_lazy_auth_val end context.schema.after_lazy(auth_val) do |is_authorized| diff --git a/spec/graphql/authorization_spec.rb b/spec/graphql/authorization_spec.rb index 1d8c329a07..01b1836e7e 100644 --- a/spec/graphql/authorization_spec.rb +++ b/spec/graphql/authorization_spec.rb @@ -966,4 +966,54 @@ def int refute res.key?("errors") end end + + describe "overriding authorized_new" do + class AuthorizedNewOverrideSchema < GraphQL::Schema + class LogTracer + def trace(key, data) + if (c = data[:context]) || ((q = data[:query]) && (c = q.context)) + c[:log] << key + end + yield + end + end + + module CustomIntrospection + class DynamicFields < GraphQL::Introspection::DynamicFields + def self.authorized_new(obj, ctx) + new(obj, ctx) + end + end + end + + class Query < GraphQL::Schema::Object + def self.authorized_new(obj, ctx) + new(obj, ctx) + end + field :int, Integer, null: false + def int; 1; end + end + + query(Query) + introspection(CustomIntrospection) + tracer(LogTracer.new) + end + + it "avoids calls to Object.authorized?" do + log = [] + res = AuthorizedNewOverrideSchema.execute("{ __typename int }", context: { log: log }) + assert_equal "Query", res["data"]["__typename"] + assert_equal 1, res["data"]["int"] + expected_log = [ + "validate", + "analyze_query", + "execute_query", + "execute_field", + "execute_field", + "execute_query_lazy" + ] + + assert_equal expected_log, log + end + end end