graph_attack 1.2.0 → 2.0.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
  SHA256:
3
- metadata.gz: dd5c8edf29d269a309957b279c0690cb46f897ca4e02c8909cd80816b9ce76fb
4
- data.tar.gz: ccf872288c199a97d55a36565998e04ca94edc982f1318bd66a67b70140aac9a
3
+ metadata.gz: 71a5e6c0ce41ca59713a49108f5ebcc3a579aefcd1e95273c495a5ae1e4fd6f6
4
+ data.tar.gz: 5430e07ebf58ac5b9addbe8b397f6fd832fa56091112136f8ce972cd2e1fe0aa
5
5
  SHA512:
6
- metadata.gz: 5d7f367fa3125069280b021f6404a19e6a5bcba197ea3c3408b066eadb593e295f2cabcb7d3d4cda5ef0b2a806ac2055dc3943559257aea208dd077a62ff468c
7
- data.tar.gz: 52adf301f34c476a6b6f97013175bc0036c588788d158d971ad553d62c86c54ac27ad1be83be6c99a120a0b90bf7b2e4da58455bd2988e9fd523ffa92956c203
6
+ metadata.gz: cef61dfd8f249877fcdbd6ae1962b6678efd280ef415989d6abe22ebb7e8db68dd164ce47ecce610548ac13352471044741c0bf6918ca050dc2193a5178ab5af
7
+ data.tar.gz: fdf832b9e228ccbf8aa66777f2f9334a7c32a25a5435e6017e0ac8073c7c636cd21dc06f0416a8abbf9f063ff30f64f209d55d9979e2eeea27fd7dff4f1ffe84
data/.rubocop.yml CHANGED
@@ -5,6 +5,7 @@ require:
5
5
  AllCops:
6
6
  TargetRubyVersion: 2.7
7
7
  DisplayCopNames: true
8
+ NewCops: enable
8
9
 
9
10
  # Do not sort gems in Gemfile, since we are grouping them by functionality.
10
11
  Bundler/OrderedGems:
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.3
1
+ 2.7.5
data/.travis.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.7.3
4
+ - 2.7.5
5
+ - 3.1.2
5
6
  services:
6
7
  - redis-server
data/CHANGELOG.md CHANGED
@@ -1,6 +1,16 @@
1
1
  unreleased
2
2
  ----------
3
3
 
4
+ v2.0.0
5
+ ------
6
+
7
+ Breaking changes:
8
+ - Drop support for GraphQL legacy schema, please use GraphQL::Ruby's class-based
9
+ syntax exclusively.
10
+
11
+ Feature:
12
+ - Support Ruby 3.
13
+
4
14
  v1.2.0
5
15
  ------
6
16
 
data/README.md CHANGED
@@ -6,36 +6,19 @@ GraphQL analyser for blocking & throttling.
6
6
 
7
7
  ## Usage
8
8
 
9
- This gem adds a method to limit access to your GraphQL fields by IP:
9
+ This gem adds a method to limit access to your GraphQL fields by IP address:
10
10
 
11
11
  ```rb
12
12
  class QueryType < GraphQL::Schema::Object
13
13
  field :some_expensive_field, String, null: false do
14
- extension(GraphAttack::RateLimit, threshold: 15, interval: 60)
14
+ extension GraphAttack::RateLimit, threshold: 15, interval: 60
15
15
  end
16
16
 
17
17
  # …
18
18
  end
19
19
  ```
20
20
 
21
- <details>
22
- <summary>If using GraphQL::Ruby's legacy schema definition</summary>
23
-
24
- ```rb
25
- QueryType = GraphQL::ObjectType.define do
26
- name 'Query'
27
-
28
- field :someExpensiveField do
29
- rate_limit threshold: 15, interval: 60
30
-
31
- # …
32
- end
33
- end
34
- ```
35
-
36
- </details>
37
-
38
- This would allow only 15 calls per minute by the same IP.
21
+ This would allow only 15 calls per minute by the same IP address.
39
22
 
40
23
  ## Requirements
41
24
 
@@ -44,11 +27,11 @@ of [Redis](https://redis.io/).
44
27
 
45
28
  ## Installation
46
29
 
47
- Add these lines to your application's `Gemfile`:
30
+ Add these lines to your applications `Gemfile`:
48
31
 
49
32
  ```ruby
50
33
  # GraphQL analyser for blocking & throttling by IP.
51
- gem 'graph_attack'
34
+ gem "graph_attack"
52
35
  ```
53
36
 
54
37
  And then execute:
@@ -57,22 +40,7 @@ And then execute:
57
40
  $ bundle
58
41
  ```
59
42
 
60
- <details>
61
- <summary>If using GraphQL::Ruby's legacy schema definition</summary>
62
-
63
- Add the query analyser to your schema:
64
-
65
- ```rb
66
- ApplicationSchema = GraphQL::Schema.define do
67
- query_analyzer GraphAttack::RateLimiter.new
68
-
69
- # …
70
- end
71
- ```
72
-
73
- </details>
74
-
75
- Finally, make sure you add the current user's IP address as `ip:` to the
43
+ Finally, make sure you add the current user’s IP address as `ip:` to the
76
44
  GraphQL context. E.g.:
77
45
 
78
46
  ```rb
@@ -96,26 +64,13 @@ Use a custom Redis client instead of the default:
96
64
 
97
65
  ```rb
98
66
  field :some_expensive_field, String, null: false do
99
- extension(
100
- GraphAttack::RateLimit,
101
- threshold: 15,
102
- interval: 60,
103
- redis_client: Redis.new(url: "…"),
104
- )
67
+ extension GraphAttack::RateLimit,
68
+ threshold: 15,
69
+ interval: 60,
70
+ redis_client: Redis.new(url: "…")
105
71
  end
106
72
  ```
107
73
 
108
- <details>
109
- <summary>If using GraphQL::Ruby's legacy schema definition</summary>
110
-
111
- ```rb
112
- query_analyzer GraphAttack::RateLimiter.new(
113
- redis_client: Redis.new(url: "…")
114
- )
115
- ```
116
-
117
- </details>
118
-
119
74
  ## Development
120
75
 
121
76
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
@@ -130,8 +85,8 @@ see the tags on this repository.
130
85
  ## Releasing
131
86
 
132
87
  To release a new version, update the version number in `version.rb`, commit,
133
- and then run `bundle exec rake release`, which will create a git tag for the
134
- version, push git commits and tags, and push the `.gem` file to
88
+ and then run `bin/rake release`, which will create a git tag for the version,
89
+ push git commits and tags, and push the gem to
135
90
  [rubygems.org](https://rubygems.org).
136
91
 
137
92
  ## Contributing
data/graph_attack.gemspec CHANGED
@@ -14,13 +14,16 @@ Gem::Specification.new do |spec|
14
14
  spec.description = 'GraphQL analyser for blocking & throttling'
15
15
  spec.homepage = 'https://github.com/sunny/graph_attack'
16
16
 
17
+ spec.metadata['rubygems_mfa_required'] = 'true'
18
+
17
19
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
20
  f.match(%r{^(test|spec|features)/})
19
21
  end
20
22
  spec.bindir = 'exe'
21
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
24
  spec.require_paths = ['lib']
23
- spec.required_ruby_version = ['>= 2.5.7', '< 2.8']
25
+
26
+ spec.required_ruby_version = ['>= 2.5.7', '< 3.2']
24
27
 
25
28
  # This gem is an analyser for the GraphQL ruby gem.
26
29
  spec.add_dependency 'graphql', '>= 1.7.9'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphAttack
4
- VERSION = '1.2.0'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/graph_attack.rb CHANGED
@@ -1,17 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'graphql'
4
3
  require 'ratelimit'
5
-
4
+ require 'graphql'
6
5
  require 'graphql/tracing'
7
6
 
8
7
  require 'graph_attack/version'
9
8
 
10
9
  # Class-based schema
11
- require 'graph_attack/rate_limit'
12
10
  require 'graph_attack/error'
11
+ require 'graph_attack/rate_limit'
13
12
  require 'graph_attack/rate_limited'
14
-
15
- # Legacy schema
16
- require 'graph_attack/rate_limiter'
17
- require 'graph_attack/metadata'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graph_attack
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fanny Cheung
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2021-07-27 00:00:00.000000000 Z
12
+ date: 2022-05-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: graphql
@@ -165,14 +165,13 @@ files:
165
165
  - graph_attack.gemspec
166
166
  - lib/graph_attack.rb
167
167
  - lib/graph_attack/error.rb
168
- - lib/graph_attack/metadata.rb
169
168
  - lib/graph_attack/rate_limit.rb
170
169
  - lib/graph_attack/rate_limited.rb
171
- - lib/graph_attack/rate_limiter.rb
172
170
  - lib/graph_attack/version.rb
173
171
  homepage: https://github.com/sunny/graph_attack
174
172
  licenses: []
175
- metadata: {}
173
+ metadata:
174
+ rubygems_mfa_required: 'true'
176
175
  post_install_message:
177
176
  rdoc_options: []
178
177
  require_paths:
@@ -184,7 +183,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
184
183
  version: 2.5.7
185
184
  - - "<"
186
185
  - !ruby/object:Gem::Version
187
- version: '2.8'
186
+ version: '3.2'
188
187
  required_rubygems_version: !ruby/object:Gem::Requirement
189
188
  requirements:
190
189
  - - ">="
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Add custom field metadata
4
- GraphQL::Field.accepts_definitions(
5
- rate_limit: GraphQL::Define.assign_metadata_key(:rate_limit),
6
- )
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GraphAttack
4
- # Query analyser you can add to your GraphQL schema to limit calls by IP.
5
- #
6
- # ApplicationSchema = GraphQL::Schema.define do
7
- # query_analyzer GraphAttack::RateLimiter.new
8
- # end
9
- #
10
- class RateLimiter
11
- class Error < StandardError; end
12
-
13
- class RateLimited < GraphQL::AnalysisError; end
14
-
15
- def initialize(redis_client: Redis.new)
16
- @redis_client = redis_client
17
- end
18
-
19
- def initial_value(query)
20
- {
21
- ip: query.context[:ip],
22
- query_rate_limits: [],
23
- }
24
- end
25
-
26
- def call(memo, visit_type, irep_node)
27
- if rate_limited_node?(visit_type, irep_node)
28
- data = rate_limit_data(irep_node)
29
-
30
- memo[:query_rate_limits].push(data)
31
-
32
- increment_rate_limit(memo[:ip], data[:key])
33
- end
34
-
35
- memo
36
- end
37
-
38
- def final_value(memo)
39
- handle_exceeded_calls_on_queries(memo)
40
- end
41
-
42
- private
43
-
44
- attr_reader :redis_client
45
-
46
- def increment_rate_limit(ip, key)
47
- raise Error, 'Missing :ip value on the GraphQL context' unless ip
48
-
49
- rate_limit(ip).add(key)
50
- end
51
-
52
- def rate_limit_data(node)
53
- data = node.definition.metadata[:rate_limit]
54
-
55
- data.merge(
56
- key: "graphql-query-#{node.name}",
57
- query_name: node.name,
58
- )
59
- end
60
-
61
- def handle_exceeded_calls_on_queries(memo)
62
- rate_limited_queries = memo[:query_rate_limits].map do |limit_data|
63
- next unless calls_exceeded_on_query?(memo[:ip], limit_data)
64
-
65
- limit_data[:query_name]
66
- end.compact
67
-
68
- return unless rate_limited_queries.any?
69
-
70
- queries = rate_limited_queries.join(', ')
71
- RateLimited.new("Query rate limit exceeded on #{queries}")
72
- end
73
-
74
- def calls_exceeded_on_query?(ip, query_limit_data)
75
- rate_limit(ip).exceeded?(
76
- query_limit_data[:key],
77
- threshold: query_limit_data[:threshold],
78
- interval: query_limit_data[:interval],
79
- )
80
- end
81
-
82
- def rate_limit(ip)
83
- @rate_limit ||= {}
84
- @rate_limit[ip] ||= Ratelimit.new(ip, redis: redis_client)
85
- end
86
-
87
- def rate_limited_node?(visit_type, node)
88
- query_field_node?(node) &&
89
- visit_type == :enter &&
90
- node.definition.metadata[:rate_limit]
91
- end
92
-
93
- def query_field_node?(node)
94
- node.owner_type.name == 'Query' &&
95
- node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
96
- end
97
- end
98
- end