graphql-guard 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b46996e864a4ef5bb6d263173c99a2550f4295a
4
- data.tar.gz: 2dbd35db68ce97fdd5505d27cbadaee2b686c9ce
3
+ metadata.gz: 5870b2ab41869305af964ae8412f7b7628328272
4
+ data.tar.gz: 35db74d61a58b98c041a6f31a3fe05f1fd2183e2
5
5
  SHA512:
6
- metadata.gz: 808c4e4702efede0b4bf662bc8b9ee590a053d2a0cd53cb11d23c25224eac82d0c826a20dccd911281b916f9b8f1ca389a9b72a68804e831a2d8b76f9ae54ef5
7
- data.tar.gz: b6ca578ecbcb68f53a5c7161ddef5a1cc4213cefa2a85bf203d16fdb5541c8d7d9d011764c9cf307a6fa69cf2bc312a853086bdc3e890db4073e97d57ef43798
6
+ metadata.gz: 984a157c13ca13f6d49b6b2c770f51d8898b573ac58b8bf9c927b7c63b38e0a9cc6a1e731a621546a1fa875aef9b0c3489323aa8f7605b503ed91bfc02fcf279
7
+ data.tar.gz: 15f06cd5cfe6d2e5fad01258010c10b4a9203fa4427344e5007107decc9583b3b4d215073acf9c1e887cdca405c55f5d3507612359b3caa81152a5820c7a34fe
data/CHANGELOG.md CHANGED
@@ -8,6 +8,14 @@ one of the following labels: `Added`, `Changed`, `Deprecated`,
8
8
  to manage the versions of this gem so
9
9
  that you can set version constraints properly.
10
10
 
11
- #### [Unreleased](https://github.com/exAspArk/graphql-guard/compare/e6d7d0f...HEAD)
11
+ #### [Unreleased](https://github.com/exAspArk/graphql-guard/compare/v0.2.0...HEAD)
12
12
 
13
13
  * WIP
14
+
15
+ #### [v0.2.0](https://github.com/exAspArk/graphql-guard/compare/v0.1.0...v0.2.0)
16
+
17
+ * `Added`: support for object policies.
18
+
19
+ #### [v0.1.0](https://github.com/exAspArk/graphql-guard/compare/e6d7d0f...v0.1.0) – 2017-07-19
20
+
21
+ * `Added`: initial functional version with inline policies.
data/Gemfile CHANGED
@@ -2,5 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
+ gem "pry"
6
+
5
7
  # Specify your gem's dependencies in graphql-guard.gemspec
6
8
  gemspec
data/README.md CHANGED
@@ -19,9 +19,9 @@ end
19
19
  # define query
20
20
  QueryType = GraphQL::ObjectType.define do
21
21
  name "Query"
22
- field :posts, [PostType] do
22
+ field :posts, !types[PostType] do
23
23
  argument :user_id, !types.ID
24
- resolve ->(_obj, args, _ctx) { Post.find(args[:user_id]) }
24
+ resolve ->(_obj, args, _ctx) { Post.where(user_id: args[:user_id]) }
25
25
  end
26
26
  end
27
27
 
@@ -38,14 +38,14 @@ GraphSchema.execute(
38
38
  )
39
39
  ```
40
40
 
41
- ### Adding graphql-guard
41
+ ### Inline policies
42
42
 
43
43
  Add `GraphQL::Guard` to your schema:
44
44
 
45
45
  ```ruby
46
46
  Schema = GraphQL::Schema.define do
47
47
  query QueryType
48
- use GraphQL::Guard.new # <========= HERE
48
+ use GraphQL::Guard.new # <======= ʘ‿ʘ
49
49
  end
50
50
  ```
51
51
 
@@ -54,27 +54,91 @@ Now you can define `guard` for a field, which will check permissions before reso
54
54
  ```ruby
55
55
  QueryType = GraphQL::ObjectType.define do
56
56
  name "Query"
57
- field :posts, [PostType] do
57
+ field :posts, !types[PostType] do
58
58
  argument :user_id, !types.ID
59
- guard ->(_obj, args, ctx) { args[:user_id] == ctx[:current_user].id } # <========= HERE
60
- resolve ->(_obj, args, _ctx) { Post.find(args[:user_id]) }
59
+ guard ->(_obj, args, ctx) { args[:user_id] == ctx[:current_user].id } # <======= ʘ‿ʘ
60
+ ...
61
61
  end
62
62
  end
63
63
  ```
64
64
 
65
- You can also define `guard` for each field in the type:
65
+ You can also define `guard`, which will be executed for all fields in the type:
66
66
 
67
67
  ```ruby
68
68
  PostType = GraphQL::ObjectType.define do
69
69
  name "Post"
70
- guard ->(post, ctx) { post.author?(ctx[:current_user]) || ctx[:current_user].admin? } # <========= HERE
71
- field :id, !types.ID
72
- field :title, !types.String
70
+ guard ->(_post, ctx) { ctx[:current_user].admin? } # <======= ʘ‿ʘ
71
+ ...
73
72
  end
74
73
  ```
75
74
 
76
75
  If `guard` block returns `false`, then it'll raise a `GraphQL::Guard::NotAuthorizedError` error.
77
76
 
77
+ ### Policy object
78
+
79
+ Alternatively, it's possible to describe all policies by using PORO (Plain Old Ruby Object), which should implement a `guard` method. For example:
80
+
81
+ ```ruby
82
+ class GraphqlPolicy
83
+ RULES = {
84
+ QueryType => {
85
+ posts: ->(_obj, args, ctx) { args[:user_id] == ctx[:current_user].id }
86
+ },
87
+ PostType => {
88
+ '*': ->(post, ctx) { ctx[:current_user].admin? }
89
+ }
90
+ }
91
+
92
+ def self.guard(type, field)
93
+ RULES.dig(type, field)
94
+ end
95
+ end
96
+ ```
97
+
98
+ Use pass this object to `GraphQL::Guard`:
99
+
100
+ ```ruby
101
+ Schema = GraphQL::Schema.define do
102
+ query QueryType
103
+ use GraphQL::Guard.new(policy_object: GraphqlPolicy) # <======= ʘ‿ʘ
104
+ end
105
+ ```
106
+
107
+ ## Order of priority
108
+
109
+ `GraphQL::Guard` will use the policy in the following order of priority:
110
+
111
+ 1. Inline policy on the field.
112
+ 2. Policy from the policy object on the field.
113
+ 3. Inline policy on the type.
114
+ 2. Policy from the policy object on the type.
115
+
116
+ ```ruby
117
+ class GraphqlPolicy
118
+ RULES = {
119
+ PostType => {
120
+ title: ->(_post, ctx) { ctx[:current_user].admin? }, # <======= 2
121
+ '*': ->(_post, ctx) { ctx[:current_user].admin? } # <======= 4
122
+ }
123
+ }
124
+
125
+ def self.guard(type, field)
126
+ RULES.dig(type, field)
127
+ end
128
+ end
129
+
130
+ PostType = GraphQL::ObjectType.define do
131
+ name "Post"
132
+ guard ->(_post, ctx) { ctx[:current_user].admin? } # <======= 3
133
+ field :title, !types.String, guard: ->(_post, _args, ctx) { ctx[:current_user].admin? } # <======= 1
134
+ end
135
+
136
+ Schema = GraphQL::Schema.define do
137
+ query QueryType
138
+ use GraphQL::Guard.new(policy_object: GraphqlPolicy)
139
+ end
140
+ ```
141
+
78
142
  ## Installation
79
143
 
80
144
  Add this line to your application's Gemfile:
data/lib/graphql/guard.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "graphql"
2
4
  require "graphql/guard/version"
3
5
 
@@ -6,36 +8,53 @@ GraphQL::Field.accepts_definitions(guard: GraphQL::Define.assign_metadata_key(:g
6
8
 
7
9
  module GraphQL
8
10
  class Guard
11
+ ANY_FIELD_NAME = :'*'
12
+
9
13
  NotAuthorizedError = Class.new(StandardError)
10
14
 
15
+ attr_reader :policy_object
16
+
17
+ def initialize(policy_object: nil)
18
+ @policy_object = policy_object
19
+ end
20
+
11
21
  def use(schema_definition)
12
22
  schema_definition.instrument(:field, self)
13
23
  end
14
24
 
15
25
  def instrument(type, field)
16
- return field unless guard_proc?(type, field)
17
- old_resolve_proc = field.resolve_proc
26
+ field_guard_proc = inline_field_guard(field) || policy_object_guard(type, field.name.to_sym)
27
+ type_guard_proc = inline_type_guard(type) || policy_object_guard(type, ANY_FIELD_NAME)
28
+ return field if !field_guard_proc && !type_guard_proc
18
29
 
19
- new_resolve_proc =
20
- if guard_proc = field.metadata[:guard]
21
- ->(object, arguments, context) do
22
- raise NotAuthorizedError.new("#{type}.#{field.name}") unless guard_proc.call(object, arguments, context)
23
- old_resolve_proc.call(object, arguments, context)
24
- end
25
- elsif guard_proc = type.metadata[:guard]
26
- ->(object, arguments, context) do
27
- raise NotAuthorizedError.new("#{type}.#{field.name}") unless guard_proc.call(object, context)
28
- old_resolve_proc.call(object, arguments, context)
30
+ old_resolve_proc = field.resolve_proc
31
+ new_resolve_proc = ->(object, arguments, context) do
32
+ authorized =
33
+ if field_guard_proc
34
+ field_guard_proc.call(object, arguments, context)
35
+ elsif type_guard_proc
36
+ type_guard_proc.call(object, context)
29
37
  end
30
- end
38
+ raise NotAuthorizedError.new("#{type}.#{field.name}") unless authorized
39
+
40
+ old_resolve_proc.call(object, arguments, context)
41
+ end
31
42
 
32
43
  field.redefine { resolve(new_resolve_proc) }
33
44
  end
34
45
 
35
46
  private
36
47
 
37
- def guard_proc?(type, field)
38
- !!(field.metadata[:guard] || type.metadata[:guard])
48
+ def policy_object_guard(type, field_name)
49
+ policy_object && policy_object.guard(type, field_name)
50
+ end
51
+
52
+ def inline_field_guard(field)
53
+ field.metadata[:guard]
54
+ end
55
+
56
+ def inline_type_guard(type)
57
+ type.metadata[:guard]
39
58
  end
40
59
  end
41
60
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GraphQL
2
4
  class Guard
3
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
4
6
  end
5
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-guard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - exAspArk