graphql-guard 0.1.0 → 0.2.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: 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