graphql-guard 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5870b2ab41869305af964ae8412f7b7628328272
4
- data.tar.gz: 35db74d61a58b98c041a6f31a3fe05f1fd2183e2
3
+ metadata.gz: 33482164f7f2f2a17ba43bc1219975ee6de6ea19
4
+ data.tar.gz: fade6ad6159072a1f3609cbc4e43b5fba2ce100b
5
5
  SHA512:
6
- metadata.gz: 984a157c13ca13f6d49b6b2c770f51d8898b573ac58b8bf9c927b7c63b38e0a9cc6a1e731a621546a1fa875aef9b0c3489323aa8f7605b503ed91bfc02fcf279
7
- data.tar.gz: 15f06cd5cfe6d2e5fad01258010c10b4a9203fa4427344e5007107decc9583b3b4d215073acf9c1e887cdca405c55f5d3507612359b3caa81152a5820c7a34fe
6
+ metadata.gz: e198c43bd8b1a4e9ce8de01db82605f1d937efbbd8f6f509e89ae1363e253ec43a70251584fa6f7ef7a44d7b89a4029322be4b93ad9e43b4d8e04a6f3a26bea1
7
+ data.tar.gz: f465007bb574373cb6d220aea2b6361d828055a69bf57e91ed5e12a6f6fb31d64043b7636e15763eaac922926618ab1e01c280456dd91b022094df6d9e300bb7
data/CHANGELOG.md CHANGED
@@ -12,7 +12,11 @@ that you can set version constraints properly.
12
12
 
13
13
  * WIP
14
14
 
15
- #### [v0.2.0](https://github.com/exAspArk/graphql-guard/compare/v0.1.0...v0.2.0)
15
+ #### [v0.3.0](https://github.com/exAspArk/graphql-guard/compare/v0.2.0...v0.3.0) – 2017-07-19
16
+
17
+ * `Added`: ability to use custom error handlers.
18
+
19
+ #### [v0.2.0](https://github.com/exAspArk/graphql-guard/compare/v0.1.0...v0.2.0) – 2017-07-19
16
20
 
17
21
  * `Added`: support for object policies.
18
22
 
data/README.md CHANGED
@@ -4,19 +4,35 @@
4
4
 
5
5
  This tiny gem provides a field-level authorization for [graphql-ruby](https://github.com/rmosolgo/graphql-ruby).
6
6
 
7
+ ## Contents
8
+
9
+ * [Usage](#usage)
10
+ * [Inline policies](#inline-policies)
11
+ * [Policy object](#policy-object)
12
+ * [Priority order](#priority-order)
13
+ * [Error handling](#error-handling)
14
+ * [Integration](#integration)
15
+ * [CanCanCan](#cancancan)
16
+ * [Pundit](#pundit)
17
+ * [Installation](#installation)
18
+ * [Development](#development)
19
+ * [Contributing](#contributing)
20
+ * [License](#license)
21
+ * [Code of Conduct](#code-of-conduct)
22
+
7
23
  ## Usage
8
24
 
9
25
  Define a GraphQL schema:
10
26
 
11
27
  ```ruby
12
- # define type
28
+ # define a type
13
29
  PostType = GraphQL::ObjectType.define do
14
30
  name "Post"
15
31
  field :id, !types.ID
16
32
  field :title, !types.String
17
33
  end
18
34
 
19
- # define query
35
+ # define a query
20
36
  QueryType = GraphQL::ObjectType.define do
21
37
  name "Query"
22
38
  field :posts, !types[PostType] do
@@ -25,17 +41,13 @@ QueryType = GraphQL::ObjectType.define do
25
41
  end
26
42
  end
27
43
 
28
- # define schema
44
+ # define a schema
29
45
  Schema = GraphQL::Schema.define do
30
46
  query QueryType
31
47
  end
32
48
 
33
49
  # execute query
34
- GraphSchema.execute(
35
- query,
36
- variables: { user_id: 1 },
37
- context: { current_user: current_user }
38
- )
50
+ GraphSchema.execute(query, variables: { user_id: 1 }, context: { current_user: current_user })
39
51
  ```
40
52
 
41
53
  ### Inline policies
@@ -85,7 +97,7 @@ class GraphqlPolicy
85
97
  posts: ->(_obj, args, ctx) { args[:user_id] == ctx[:current_user].id }
86
98
  },
87
99
  PostType => {
88
- '*': ->(post, ctx) { ctx[:current_user].admin? }
100
+ '*': ->(_post, ctx) { ctx[:current_user].admin? }
89
101
  }
90
102
  }
91
103
 
@@ -95,7 +107,7 @@ class GraphqlPolicy
95
107
  end
96
108
  ```
97
109
 
98
- Use pass this object to `GraphQL::Guard`:
110
+ Pass this object to `GraphQL::Guard`:
99
111
 
100
112
  ```ruby
101
113
  Schema = GraphQL::Schema.define do
@@ -104,7 +116,7 @@ Schema = GraphQL::Schema.define do
104
116
  end
105
117
  ```
106
118
 
107
- ## Order of priority
119
+ ## Priority order
108
120
 
109
121
  `GraphQL::Guard` will use the policy in the following order of priority:
110
122
 
@@ -139,6 +151,96 @@ Schema = GraphQL::Schema.define do
139
151
  end
140
152
  ```
141
153
 
154
+ ## Error handling
155
+
156
+ By default `GraphQL::Guard` raises a `GraphQL::Guard::NotAuthorizedError` exception if access to field is not authorized.
157
+ You can change this behavior, by passing custom `not_authorized` lambda. For example:
158
+
159
+ ```ruby
160
+ SchemaWithoutExceptions = GraphQL::Schema.define do
161
+ query QueryType
162
+ use GraphQL::Guard.new(
163
+ # by default it raises an error
164
+ # not_authorized: ->(type, field) { raise GraphQL::Guard::NotAuthorizedError.new("#{type}.#{field}") }
165
+
166
+ # returns an error in the response
167
+ not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") }
168
+ )
169
+ end
170
+ ```
171
+
172
+ In this case executing a query will continue, but return `nil` for not authorized field and also an array of `errors`:
173
+
174
+ ```ruby
175
+ SchemaWithoutExceptions.execute("query { posts(user_id: 1) { id title } }")
176
+ # => {
177
+ # "data" => nil,
178
+ # "errors" => [{ "messages" => "Not authorized to access Query.posts", "locations": { ... }, "path" => ["posts"] }]
179
+ # }
180
+ ```
181
+
182
+ ## Integration
183
+
184
+ You can simply reuse your existing policies if you really want. You don't need any monkey patches or magic for it ;)
185
+
186
+ ### CanCanCan
187
+
188
+ ```ruby
189
+ # define an ability
190
+ class Ability
191
+ include CanCan::Ability
192
+
193
+ def initialize(user)
194
+ user ||= User.new # guest user if not logged in
195
+ if user.admin?
196
+ can :manage, :all
197
+ else
198
+ can :read, Post, author_id: user.id
199
+ end
200
+ end
201
+ end
202
+
203
+ # use the ability in your guard policy object
204
+ class GraphqlPolicy
205
+ RULES = {
206
+ PostType => {
207
+ '*': ->(post, ctx) { ctx[:current_ability].can?(:read, post) },
208
+ }
209
+ }
210
+
211
+ def self.guard(type, field)
212
+ RULES.dig(type, field)
213
+ end
214
+ end
215
+
216
+ # pass the ability
217
+ GraphSchema.execute(query, context: { current_ability: Ability.new(current_user) })
218
+ ```
219
+
220
+ ### Pundit
221
+
222
+ ```ruby
223
+ # define a policy
224
+ class PostPolicy < ApplicationPolicy
225
+ def show?
226
+ user.admin? || record.author_id == user.id
227
+ end
228
+ end
229
+
230
+ # use the policy in your guard policy object
231
+ class GraphqlPolicy
232
+ RULES = {
233
+ PostType => {
234
+ '*': ->(post, ctx) { PostPolicy.new(ctx[:current_user], post).show? },
235
+ }
236
+ }
237
+
238
+ def self.guard(type, field)
239
+ RULES.dig(type, field)
240
+ end
241
+ end
242
+ ```
243
+
142
244
  ## Installation
143
245
 
144
246
  Add this line to your application's Gemfile:
data/lib/graphql/guard.rb CHANGED
@@ -9,13 +9,15 @@ GraphQL::Field.accepts_definitions(guard: GraphQL::Define.assign_metadata_key(:g
9
9
  module GraphQL
10
10
  class Guard
11
11
  ANY_FIELD_NAME = :'*'
12
+ DEFAULT_NOT_AUTHORIZED = ->(type, field) { raise NotAuthorizedError.new("#{type}.#{field}") }
12
13
 
13
14
  NotAuthorizedError = Class.new(StandardError)
14
15
 
15
- attr_reader :policy_object
16
+ attr_reader :policy_object, :not_authorized
16
17
 
17
- def initialize(policy_object: nil)
18
+ def initialize(policy_object: nil, not_authorized: DEFAULT_NOT_AUTHORIZED)
18
19
  @policy_object = policy_object
20
+ @not_authorized = not_authorized
19
21
  end
20
22
 
21
23
  def use(schema_definition)
@@ -35,9 +37,12 @@ module GraphQL
35
37
  elsif type_guard_proc
36
38
  type_guard_proc.call(object, context)
37
39
  end
38
- raise NotAuthorizedError.new("#{type}.#{field.name}") unless authorized
39
40
 
40
- old_resolve_proc.call(object, arguments, context)
41
+ if authorized
42
+ old_resolve_proc.call(object, arguments, context)
43
+ else
44
+ not_authorized.call(type, field.name.to_sym)
45
+ end
41
46
  end
42
47
 
43
48
  field.redefine { resolve(new_resolve_proc) }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  class Guard
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
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.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - exAspArk