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 +4 -4
- data/CHANGELOG.md +9 -1
- data/Gemfile +2 -0
- data/README.md +75 -11
- data/lib/graphql/guard.rb +34 -15
- data/lib/graphql/guard/version.rb +3 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5870b2ab41869305af964ae8412f7b7628328272
|
4
|
+
data.tar.gz: 35db74d61a58b98c041a6f31a3fe05f1fd2183e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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
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.
|
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
|
-
###
|
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 #
|
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 } #
|
60
|
-
|
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
|
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 ->(
|
71
|
-
|
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
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
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
|
38
|
-
|
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
|