diff --git a/README.md b/README.md index 4c34efd..0fc4cea 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ This gem provides a field-level authorization for [graphql-ruby](https://github. * [Inline policies](#inline-policies) * [Policy object](#policy-object) * [Priority order](#priority-order) -* [Error handling](#error-handling) * [Integration](#integration) * [CanCanCan](#cancancan) * [Pundit](#pundit) +* [Error handling](#error-handling) * [Installation](#installation) * [Testing](#testing) * [Development](#development) @@ -39,7 +39,7 @@ PostType = GraphQL::ObjectType.define do name "Post" field :id, !types.ID - field :title, !types.String + field :title, types.String end # Define a query @@ -163,34 +163,6 @@ Schema = GraphQL::Schema.define do end -## Error handling - -By default `GraphQL::Guard` raises a `GraphQL::Guard::NotAuthorizedError` exception if access to field is not authorized. -You can change this behavior, by passing custom `not_authorized` lambda. For example: - -
-SchemaWithErrors = GraphQL::Schema.define do - query QueryType - use GraphQL::Guard.new( - # Returns an error in the response - not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") } - - # By default it raises an error - # not_authorized: ->(type, field) { raise GraphQL::Guard::NotAuthorizedError.new("#{type}.#{field}") } - ) -end -- -In this case executing a query will continue, but return `nil` for not authorized field and also an array of `errors`: - -
-SchemaWithErrors.execute("query { posts(user_id: 1) { id title } }") -# => { -# "data" => nil, -# "errors" => [{ "messages" => "Not authorized to access Query.posts", "locations": { ... }, "path" => ["posts"] }] -# } -- ## Integration You can simply reuse your existing policies if you really want. You don't need any monkey patches or magic for it ;) @@ -244,6 +216,74 @@ end Schema.execute(query, context: { current_user: current_user }) +## Error handling + +By default `GraphQL::Guard` raises a `GraphQL::Guard::NotAuthorizedError` exception if access to the field is not authorized. +You can change this behavior, by passing custom `not_authorized` lambda. For example: + +
+SchemaWithErrors = GraphQL::Schema.define do + query QueryType + use GraphQL::Guard.new( + # By default it raises an error + # not_authorized: ->(type, field) { raise GraphQL::Guard::NotAuthorizedError.new("#{type}.#{field}") } + + # Returns an error in the response + not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") } + ) +end ++ +In this case executing a query will continue, but return `nil` for not authorized field and also an array of `errors`: + +
+SchemaWithErrors.execute("query { posts(user_id: 1) { id title } }") +# => { +# "data" => nil, +# "errors" => [{ +# "messages" => "Not authorized to access Query.posts", +# "locations": { "line" => 1, "column" => 9 }, +# "path" => ["posts"] +# }] +# } ++ +In more advanced cases, you may want not to return `errors` only for some unauthorized fields. Simply return `nil` if user is not authorized to access the field. You can achieve it, for example, by placing the logic into your `PolicyObject`: + +
+class GraphqlPolicy + RULES = { + PostType => { + '*': { + guard: ->(obj, args, ctx) { ... }, + not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") } + } + title: { + guard: ->(obj, args, ctx) { .. }, + not_authorized: ->(type, field) { nil } # simply return nil if not authorized, no errors + } + } + } + + def self.guard(type, field) + RULES.dig(type, field, :guard) + end +end + +Schema = GraphQL::Schema.define do + query QueryType + mutation MutationType + + use GraphQL::Guard.new( + policy_object: GraphqlPolicy, + not_authorized: ->(field, type) { + handler = GraphqlPolicy::RULES.dig(type, field, :not_authorized) + handler&.call(type, field) + } + ) +end ++ ## Installation Add this line to your application's Gemfile: diff --git a/spec/fixtures/inline_schema.rb b/spec/fixtures/inline_schema.rb index fc557bf..64926e5 100644 --- a/spec/fixtures/inline_schema.rb +++ b/spec/fixtures/inline_schema.rb @@ -5,7 +5,7 @@ module Inline name "Post" guard ->(_post, _args, ctx) { ctx[:current_user].admin? } field :id, !types.ID - field :title, !types.String + field :title, types.String end QueryType = GraphQL::ObjectType.define do diff --git a/spec/fixtures/policy_object_schema.rb b/spec/fixtures/policy_object_schema.rb index b705e95..246f4c8 100644 --- a/spec/fixtures/policy_object_schema.rb +++ b/spec/fixtures/policy_object_schema.rb @@ -4,7 +4,7 @@ module PolicyObject PostType = GraphQL::ObjectType.define do name "Post" field :id, !types.ID - field :title, !types.String + field :title, types.String end QueryType = GraphQL::ObjectType.define do