apigatewayv2_rack 0.1.3 → 0.2.1

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: b9c5a5dcc23b7a6e524d7a8c847befb580b8771839ae013c977991e35db824ef
4
- data.tar.gz: e33794f5ffae56ad06cb14f12fcac3b3a41ad5e33f6119a9c262756801d9f3b6
3
+ metadata.gz: dcad9826619202901586bc17c2dddd75779e75bbfdb194a461c853c49fc8798d
4
+ data.tar.gz: c06b602502a62c1e069d3d88c5180afa47660db4344618d73c311a3503a77f3e
5
5
  SHA512:
6
- metadata.gz: 6a27469fcbc9c1bc754aca949ed94fce1ec83f4e1cd5909fa70bee771267dd480ef12f78d69508205913f88c23bc4082a149279975ec6e5ed8897c0d5e777fbd
7
- data.tar.gz: 88ff099fa17b319cf2f1cedd08091eeeff14874a7c9fa9eac37430e4ed98b2e51b7e2f1b2bf6146cb215730c637cbc639016ecd1b64e6759e96c26892c541491
6
+ metadata.gz: 9d45ecda50b157c99dc848d12c79a8bd9e4dc6d9d5f649f8908facfb3e8854bd9974be14427ab51af5b309070b94030b316a600b669066888a955ee6f1cb9ed7
7
+ data.tar.gz: 2507eb06ba132526f2e405538f3fd07adedb03c53051798a3e3cccae35b87c9bbc3838c5876f4efb63975b1e9ef3e76e1842da2bf86492e84b224aba58de013b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.1] - 2025-03-07
4
+
5
+ - Address `Rack::VERSION` removal.
6
+
7
+ ## [0.2.0] - 2023-03-24
8
+
9
+ - `Apigatewayv2Rack.handle_request` now takes a block and pass rack env and `Apigatewayv2Rack::Request` object to allow final modification before passing env to a Rack app.
10
+ - `Apigatewayv2Rack.generate_handler` and `handler_from_rack_config_file` propagates given block to `handle_request` for the enhancement above.
11
+ - Introduce `Apigatewayv2Rack::Middlewares::CloudfrontXff` and `Apigatewayv2Rack::Middlewares::CloudfrontVerify` as a helper middleware.
12
+
3
13
  ## [0.1.3] - 2023-03-22
4
14
 
5
15
  - Fixed Errno::EACCES from StringIO when a streaming body (body does not respond to `#each`) is returned
data/Gemfile.lock CHANGED
@@ -1,28 +1,32 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- apigatewayv2_rack (0.1.3)
4
+ apigatewayv2_rack (0.2.1)
5
+ base64
5
6
  rack
7
+ stringio
6
8
 
7
9
  GEM
8
10
  remote: https://rubygems.org/
9
11
  specs:
10
- diff-lcs (1.5.0)
11
- rack (3.0.7)
12
- rake (13.0.6)
13
- rspec (3.11.0)
14
- rspec-core (~> 3.11.0)
15
- rspec-expectations (~> 3.11.0)
16
- rspec-mocks (~> 3.11.0)
17
- rspec-core (3.11.0)
18
- rspec-support (~> 3.11.0)
19
- rspec-expectations (3.11.1)
12
+ base64 (0.2.0)
13
+ diff-lcs (1.6.0)
14
+ rack (3.1.11)
15
+ rake (13.2.1)
16
+ rspec (3.13.0)
17
+ rspec-core (~> 3.13.0)
18
+ rspec-expectations (~> 3.13.0)
19
+ rspec-mocks (~> 3.13.0)
20
+ rspec-core (3.13.3)
21
+ rspec-support (~> 3.13.0)
22
+ rspec-expectations (3.13.3)
20
23
  diff-lcs (>= 1.2.0, < 2.0)
21
- rspec-support (~> 3.11.0)
22
- rspec-mocks (3.11.1)
24
+ rspec-support (~> 3.13.0)
25
+ rspec-mocks (3.13.2)
23
26
  diff-lcs (>= 1.2.0, < 2.0)
24
- rspec-support (~> 3.11.0)
25
- rspec-support (3.11.1)
27
+ rspec-support (~> 3.13.0)
28
+ rspec-support (3.13.2)
29
+ stringio (3.1.5)
26
30
 
27
31
  PLATFORMS
28
32
  ruby
@@ -33,4 +37,4 @@ DEPENDENCIES
33
37
  rspec (~> 3.0)
34
38
 
35
39
  BUNDLED WITH
36
- 2.3.16
40
+ 2.3.21
data/README.md CHANGED
@@ -43,6 +43,13 @@ p resp.as_json
43
43
 
44
44
  See [./Dockerfile.integration](./Dockerfile.integration) and [./integration](./integration).
45
45
 
46
+ ### Middlewares
47
+
48
+ This gem includes several utility middlewares:
49
+
50
+ - [CloudfrontVerify](./lib/apigatewayv2_rack/middlewares/cloudfront_verify.rb): Verify `x-origin-verify` value to protect unwanted direct access.
51
+ - [CloudfrontXff](./lib/apigatewayv2_rack/middlewares/cloudfront_xff.rb): Respect `cloudfront-viewer-address` as `x-forwarded-for` value.
52
+
46
53
  ## Development
47
54
 
48
55
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -31,6 +31,8 @@ Gem::Specification.new do |spec|
31
31
  # Uncomment to register a new dependency of your gem
32
32
  # spec.add_dependency "example-gem", "~> 1.0"
33
33
  spec.add_dependency 'rack'
34
+ spec.add_dependency 'base64'
35
+ spec.add_dependency 'stringio'
34
36
 
35
37
  # For more information and examples about making a new gem, check out our
36
38
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ require 'rack/utils'
3
+
4
+ module Apigatewayv2Rack
5
+ module Middlewares
6
+ # Compare X-Origin-Verify header matches the expected value and otherwise returns 403.
7
+ # This is useful to use with CloudFront's origin custom request header to protect from direct access to function.
8
+ #
9
+ # See also: https://www.wellarchitectedlabs.com/security/300_labs/300_multilayered_api_security_with_cognito_and_waf/3_prevent_requests_from_accessing_api_directly/
10
+ class CloudfrontVerify
11
+ # +value+ is an expected string value of x-origin-verify.
12
+ def initialize(app, value)
13
+ @app = app
14
+ @value = value
15
+ end
16
+
17
+ def env_name
18
+ 'HTTP_X_ORIGIN_VERIFY'
19
+ end
20
+
21
+ def call(env)
22
+ given = env[env_name]
23
+
24
+ unless given && Rack::Utils.secure_compare(given, @value)
25
+ env['rack.logger']&.warn("#{self.class.name} protected unwanted access from #{env['REMOTE_ADDR'].inspect}")
26
+ return [401, {'Content-Type' => 'text/plain'}, ['Unauthorized']]
27
+ end
28
+
29
+ @app.call(env)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apigatewayv2Rack
4
+ module Middlewares
5
+ # Apigatewayv2Rack::Middlewares::CloudfrontXff transforms cloudfront-viewer-address to x-forwarded-for value.
6
+ # It is recommended to use with Apigatewayv2Rack::Middlewares::CloudfrontVerify.
7
+ class CloudfrontXff
8
+ # When +replace_remote_addr_with+ is set, REMOTE_ADDR will be replaced with given value; this
9
+ # allows Rack::Request#ip to respect xff on its default ip_filter. Default to 127.0.0.1.
10
+ def initialize(app, replace_remote_addr_with: '127.0.0.1')
11
+ @app = app
12
+ @replace_remote_addr_with = replace_remote_addr_with
13
+ end
14
+
15
+ V6_REGEXP = /^([a-f0-9:]+):(\d+)$/
16
+
17
+ def call(env)
18
+ viewer = env['HTTP_CLOUDFRONT_VIEWER_ADDRESS']
19
+ if viewer
20
+ addr,port = if viewer.include?('.')
21
+ viewer.split(?:, 2)
22
+ else
23
+ viewer.downcase.match(V6_REGEXP)&.to_a[1,2]
24
+ end
25
+
26
+ if addr && port
27
+ env['HTTP_X_APIGATEWAYV2RACK_ORIG_X_FORWARDED_FOR'] = env['HTTP_X_FORWARDED_FOR'] if env['HTTP_X_FORWARDED_FOR']
28
+ env['HTTP_X_APIGATEWAYV2RACK_ORIG_X_FORWARDED_PORT'] = env['HTTP_X_FORWARDED_PORT'] if env['HTTP_X_FORWARDED_PORT']
29
+ env['HTTP_X_FORWARDED_FOR'] = addr
30
+ env['HTTP_X_FORWARDED_PORT'] = port
31
+
32
+ if @replace_remote_addr_with
33
+ env['HTTP_X_APIGATEWAYV2RACK_ORIG_REMOTE_ADDR'] = env['REMOTE_ADDR'] if env['REMOTE_ADDR']
34
+ env['REMOTE_ADDR'] = @replace_remote_addr_with
35
+ end
36
+ end
37
+ end
38
+
39
+ @app.call(env)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -81,6 +81,8 @@ module Apigatewayv2Rack
81
81
  end
82
82
  end
83
83
 
84
+ RACK_VERSION = defined?(Rack::VERSION) ? Rack::VERSION : Rack.release
85
+
84
86
  def to_h
85
87
  {
86
88
  'SERVER_PROTOCOL' => protocol,
@@ -93,7 +95,7 @@ module Apigatewayv2Rack
93
95
  'CONTENT_LENGTH' => body.bytesize.to_s,
94
96
  'CONTENT_TYPE' => headers['content-type'] || '',
95
97
  'REMOTE_ADDR' => source_ip,
96
- 'rack.version' => Rack::VERSION,
98
+ 'rack.version' => RACK_VERSION,
97
99
  'rack.url_scheme' => (use_x_forwarded_host && headers['x-forwarded-proto']) || 'https',
98
100
  'rack.input' => StringIO.new(body),
99
101
  'rack.errors' => $stderr,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Apigatewayv2Rack
4
- VERSION = "0.1.3"
4
+ VERSION = "0.2.1"
5
5
  end
@@ -5,30 +5,41 @@ require_relative "apigatewayv2_rack/error"
5
5
  require_relative "apigatewayv2_rack/request"
6
6
  require_relative "apigatewayv2_rack/response"
7
7
 
8
+ require_relative "apigatewayv2_rack/middlewares/cloudfront_xff"
9
+ require_relative "apigatewayv2_rack/middlewares/cloudfront_verify"
10
+
8
11
  module Apigatewayv2Rack
9
12
  # Takes Rack +app+, Lambda +event+ and +context+ of API Gateway V2 event and
10
13
  # returns a HTTP response from +app+ as API Gateway V2 Lambda event format.
11
- def self.handle_request(app:, event:, context:, request_options: {})
14
+ #
15
+ # When block is given, converted Rack env will be passed to make some final
16
+ # modification before passing it to an +app+.
17
+ def self.handle_request(app:, event:, context:, request_options: {}, &block)
12
18
  req = Request.new(event, context, **request_options)
13
- status, headers, body = app.call(req.to_h)
19
+ env = req.to_h
20
+ block&.call(env, req)
21
+ status, headers, body = app.call(env)
14
22
  Response.new(status: status, headers: headers, body: body, elb: req.elb?, multivalued: req.multivalued?).as_json
15
23
  end
16
24
 
17
25
  module Handler
18
- attr_reader :app
19
- def handle(event:, context:)
20
- Apigatewayv2Rack.handle_request(event: event, context: context, app: @app)
26
+ attr_reader :app
27
+ attr_reader :block
28
+ def handle(event:, context:, &givenblock)
29
+ b = givenblock || @block
30
+ Apigatewayv2Rack.handle_request(event: event, context: context, app: @app, &b)
21
31
  end
22
32
  end
23
33
 
24
- def self.generate_handler(app)
34
+ def self.generate_handler(app, &block)
25
35
  m = Module.new
26
36
  m.extend(Handler)
27
37
  m.instance_variable_set(:@app, app)
38
+ m.instance_variable_set(:@block, block)
28
39
  m
29
40
  end
30
41
 
31
- def self.handler_from_rack_config_file(path = './config.ru')
42
+ def self.handler_from_rack_config_file(path = './config.ru', &block)
32
43
  require 'rack'
33
44
  require 'rack/builder'
34
45
  app = if Rack.release[0] == '2'
@@ -36,6 +47,6 @@ module Apigatewayv2Rack
36
47
  else
37
48
  Rack::Builder.load_file(path)
38
49
  end
39
- generate_handler(app)
50
+ generate_handler(app, &block)
40
51
  end
41
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apigatewayv2_rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sorah Fukumori
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-21 00:00:00.000000000 Z
11
+ date: 2025-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: base64
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: stringio
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  description:
28
56
  email:
29
57
  - her@sorah.jp
@@ -50,6 +78,8 @@ files:
50
78
  - integration/template.jsonnet
51
79
  - lib/apigatewayv2_rack.rb
52
80
  - lib/apigatewayv2_rack/error.rb
81
+ - lib/apigatewayv2_rack/middlewares/cloudfront_verify.rb
82
+ - lib/apigatewayv2_rack/middlewares/cloudfront_xff.rb
53
83
  - lib/apigatewayv2_rack/request.rb
54
84
  - lib/apigatewayv2_rack/response.rb
55
85
  - lib/apigatewayv2_rack/version.rb
@@ -76,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
106
  - !ruby/object:Gem::Version
77
107
  version: '0'
78
108
  requirements: []
79
- rubygems_version: 3.4.0.dev
109
+ rubygems_version: 3.4.6
80
110
  signing_key:
81
111
  specification_version: 4
82
112
  summary: handle AWS Lambda API Gateway V2 or ALB (ELBv2) lambda request event with