Skip to content

Commit

Permalink
Merge 538a157 into 9b0d117
Browse files Browse the repository at this point in the history
  • Loading branch information
exAspArk committed Apr 8, 2020
2 parents 9b0d117 + 538a157 commit 608b463
Show file tree
Hide file tree
Showing 14 changed files with 223 additions and 348 deletions.
36 changes: 9 additions & 27 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,11 @@
sudo: false
language: ruby
before_install: gem install bundler -v 1.17.3
matrix:
include:
- gemfile: graphql-1.7.gemfile
env: GRAPHQL_RUBY_VERSION=1_7 CI=true
rvm: 2.3.8
- gemfile: graphql-latest.gemfile
env: GRAPHQL_RUBY_VERSION=LATEST CI=true
rvm: 2.3.8
- gemfile: graphql-1.7.gemfile
env: GRAPHQL_RUBY_VERSION=1_7 CI=true
rvm: 2.4.5
- gemfile: graphql-latest.gemfile
env: GRAPHQL_RUBY_VERSION=LATEST CI=true
rvm: 2.4.5
- gemfile: graphql-1.7.gemfile
env: GRAPHQL_RUBY_VERSION=1_7 CI=true
rvm: 2.5.7
- gemfile: graphql-latest.gemfile
env: GRAPHQL_RUBY_VERSION=LATEST CI=true
rvm: 2.5.7
- gemfile: graphql-1.7.gemfile
env: GRAPHQL_RUBY_VERSION=1_7 CI=true
rvm: 2.6.5
- gemfile: graphql-latest.gemfile
env: GRAPHQL_RUBY_VERSION=LATEST CI=true
rvm: 2.6.5
before_install: gem install bundler -v 2.1.4
rvm:
- 2.3.8
- 2.4.9
- 2.5.7
- 2.6.5
- 2.7.0
env:
- CI=true
5 changes: 3 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
source "https://rubygems.org"

gem "pry"

gem "graphql", "~> 1.10"

gem "pry"
gem 'coveralls'

# Specify your gem's dependencies in graphql-guard.gemspec
gemspec
118 changes: 53 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,38 +36,41 @@ Define a GraphQL schema:

```ruby
# Define a type
PostType = GraphQL::ObjectType.define do
name "Post"

field :id, !types.ID
field :title, types.String
class PostType < GraphQL::Schema::Object
field :id, ID, null: false
field :title, String, null: true
end

# Define a query
QueryType = GraphQL::ObjectType.define do
name "Query"
class QueryType < GraphQL::Schema::Object
field :posts, [PostType], null: false do
argument :user_id, ID, required: true
end

field :posts, !types[!PostType] do
argument :user_id, !types.ID
resolve ->(obj, args, ctx) { Post.where(user_id: args[:user_id]) }
def posts(user_id:)
Post.where(user_id: user_id)
end
end

# Define a schema
Schema = GraphQL::Schema.define do
class Schema < GraphQL::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
query QueryType
end

# Execute query
Schema.execute(query, variables: { user_id: 1 }, context: { current_user: current_user })
Schema.execute(query, variables: { userId: 1 }, context: { current_user: current_user })
```

### Inline policies

Add `GraphQL::Guard` to your schema:

<pre>
Schema = GraphQL::Schema.define do
class Schema < GraphQL::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
query QueryType
<b>use GraphQL::Guard.new</b>
end
Expand All @@ -76,22 +79,19 @@ end
Now you can define `guard` for a field, which will check permissions before resolving the field:

<pre>
QueryType = GraphQL::ObjectType.define do
name "Query"

<b>field :posts</b>, !types[!PostType] do
argument :user_id, !types.ID
class QueryType < GraphQL::Schema::Object
<b>field :posts</b>, [PostType], null: false do
argument :user_id, ID, required: true
<b>guard ->(obj, args, ctx) {</b> args[:user_id] == ctx[:current_user].id <b>}</b>
...
end
...
end
</pre>

You can also define `guard`, which will be executed for every `*` field in the type:

<pre>
PostType = GraphQL::ObjectType.define do
name "Post"
class PostType < GraphQL::Schema::Object
<b>guard ->(obj, args, ctx) {</b> ctx[:current_user].admin? <b>}</b>
...
end
Expand Down Expand Up @@ -120,27 +120,12 @@ class <b>GraphqlPolicy</b>
end
</pre>

With `graphql-ruby` gem version >= 1.8 and class-based type definitions, use `camelCased` field names in the policy object.
You'd also need to use `type.metadata` (related to [rmosolgo/graphql-ruby#1429](https://github.com/rmosolgo/graphql-ruby/issues/1429)) to get the type class:

<pre>
class GraphqlPolicy
RULES = {
MutationType => {
<b>createPost</b>: ->(obj, args, cts) { ctx[:current_user].admin? }
}
}

def self.guard(type, field)
RULES.dig(<b>type.metadata[:type_class]</b>, field)
end
end
</pre>

Pass this object to `GraphQL::Guard`:

<pre>
Schema = GraphQL::Schema.define do
class Schema < GraphQL::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
query QueryType
use GraphQL::Guard.new(<b>policy_object: GraphqlPolicy</b>)
end
Expand All @@ -167,8 +152,8 @@ end
class <b>GraphqlPolicy</b>
RULES = {
PostType => {
<b>'*': ->(obj, args, ctx) {</b> ctx[:current_user].admin? <b>}</b>, # <=== <b>4</b>
<b>title: ->(obj, args, ctx) {</b> ctx[:current_user].admin? <b>}</b> # <=== <b>2</b>
<b>'*': ->(obj, args, ctx) {</b> ctx[:current_user].admin? <b>}</b>, # <=== <b>4</b>
<b>title: ->(obj, args, ctx) {</b> ctx[:current_user].admin? <b>}</b> # <=== <b>2</b>
}
}

Expand All @@ -177,13 +162,14 @@ class <b>GraphqlPolicy</b>
end
end

PostType = GraphQL::ObjectType.define do
name "Post"
<b>guard ->(obj, args, ctx) {</b> ctx[:current_user].admin? <b>}</b> # <=== <b>3</b>
<b>field :title</b>, !types.String, <b>guard: ->(obj, args, ctx) {</b> ctx[:current_user].admin? <b>}</b> # <=== <b>1</b>
class PostType < GraphQL::Schema::Object
<b>guard ->(obj, args, ctx) {</b> ctx[:current_user].admin? <b>}</b> # <=== <b>3</b>
field :title, String, null: true, <b>guard: ->(obj, args, ctx) {</b> ctx[:current_user].admin? <b>}</b> # <=== <b>1</b>
end

Schema = GraphQL::Schema.define do
class Schema < GraphQL::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
query QueryType
use GraphQL::Guard.new(<b>policy_object: GraphqlPolicy</b>)
end
Expand Down Expand Up @@ -211,8 +197,7 @@ class <b>Ability</b>
end

# Use the ability in your guard
PostType = GraphQL::ObjectType.define do
name "Post"
class PostType < GraphQL::Schema::Object
guard ->(post, args, ctx) { <b>ctx[:current_ability].can?(:read, post)</b> }
...
end
Expand All @@ -232,8 +217,7 @@ class <b>PostPolicy</b> < ApplicationPolicy
end

# Use the ability in your guard
PostType = GraphQL::ObjectType.define do
name "Post"
class PostType < GraphQL::Schema::Object
guard ->(post, args, ctx) { <b>PostPolicy.new(ctx[:current_user], post).show?</b> }
...
end
Expand All @@ -248,14 +232,20 @@ By default `GraphQL::Guard` raises a `GraphQL::Guard::NotAuthorizedError` except
You can change this behavior, by passing custom `not_authorized` lambda. For example:

<pre>
SchemaWithErrors = GraphQL::Schema.define do
class SchemaWithErrors < GraphQL::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
query QueryType
use GraphQL::Guard.new(
# By default it raises an error
# not_authorized: ->(type, field) { raise GraphQL::Guard::NotAuthorizedError.new("#{type}.#{field}") }
# not_authorized: ->(type, field) do
# raise GraphQL::Guard::NotAuthorizedError.new("#{type.graphql_definition}.#{field}")
# end

# Returns an error in the response
<b>not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") }</b>
<b>not_authorized: ->(type, field) do
GraphQL::ExecutionError.new("Not authorized to access #{type.graphql_definition}.#{field}")
end</b>
)
end
</pre>
Expand Down Expand Up @@ -300,7 +290,9 @@ class <b>GraphqlPolicy</b>
end
end

Schema = GraphQL::Schema.define do
class Schema < GraphQL::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
query QueryType
mutation MutationType

Expand All @@ -319,11 +311,9 @@ end
It's possible to hide fields from being introspectable and accessible based on the context. For example:

<pre>
PostType = GraphQL::ObjectType.define do
name "Post"

field :id, !types.ID
field :title, types.String do
class PostType < GraphQL::Schema::Object
field :id, ID, null: false
field :title, String, null: true do
# The field "title" is accessible only for beta testers
<b>mask ->(ctx) {</b> ctx[:current_user].beta_tester? <b>}</b>
end
Expand Down Expand Up @@ -352,9 +342,8 @@ It's possible to test fields with `guard` in isolation:

<pre>
# Your type
QueryType = GraphQL::ObjectType.define do
name "Query"
<b>field :posts</b>, !types[!PostType], <b>guard ->(obj, args, ctx) {</b> ... <b>}</b>
class QueryType < GraphQL::Schema::Object
field :posts, [PostType], null: false, <b>guard ->(obj, args, ctx) {</b> ... <b>}</b>
end

# Your test
Expand All @@ -370,9 +359,8 @@ If you would like to test your fields with policy objects:

<pre>
# Your type
QueryType = GraphQL::ObjectType.define do
name "Query"
<b>field :posts</b>, !types[!PostType]
class QueryType < GraphQL::Schema::Object
field :posts, [PostType], null: false
end

# Your policy object
Expand Down
8 changes: 0 additions & 8 deletions graphql-1.7.gemfile

This file was deleted.

6 changes: 3 additions & 3 deletions graphql-guard.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ Gem::Specification.new do |spec|

spec.required_ruby_version = '>= 2.1.0' # keyword args

spec.add_runtime_dependency "graphql", ">= 1.6.0", "< 2"
spec.add_runtime_dependency "graphql", ">= 1.10.0", "< 2"

spec.add_development_dependency "bundler", "~> 1.15"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "bundler", "~> 2.1"
spec.add_development_dependency "rake", "~> 13.0"
spec.add_development_dependency "rspec", "~> 3.0"
end
8 changes: 0 additions & 8 deletions graphql-latest.gemfile

This file was deleted.

0 comments on commit 608b463

Please sign in to comment.