rack-request_replication 0.0.3 → 0.0.5

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: f066360d1270ffe1fd8bf8c0f6045c40884e7c4b
4
- data.tar.gz: 3cb0501284af15b165271922a71627b418873907
3
+ metadata.gz: f6ae7943af00005747ede7913cb2a8ec935ac80e
4
+ data.tar.gz: 469f4eda7bd661a49dd74ac05f57a7085c0e871e
5
5
  SHA512:
6
- metadata.gz: d38e8f9f3e53f44c2df81d9ee16b9e8459e54b8a8c454ef5c9abe2e2dbd5c8276dbcaecca6342d10421c616c942ce7e16f27bda9f03995223f5671c41d8c85ef
7
- data.tar.gz: aad6649afb76b4b38beab7586e24fedf87640d66a98b35e98ad4b42e4b45d34c427b84064ace3f839a95088dd39400f0c1b8e03ca601a5e6fd014732169f6c5c
6
+ metadata.gz: 513b0e735a59e0acade09e128779bd4a132613e809fb7a1d5db9decb2d51f0e964b70776e05489dc77d24a20a004c4252a56dda915107e002c9a91f2f64d0f87
7
+ data.tar.gz: 467c2ba93881da20eea3d9567eb21039b59f87d3eacb409c27fc0ad86b8c1af014644ac12a4bec00282d9b6d026d1b44bdade62a08f4ad3f077e04ce32c08c0f
data/README.md CHANGED
@@ -1,17 +1,62 @@
1
1
  # Rack::RequestReplication - Replicate Rack app HTTP requests
2
2
 
3
3
  Replicate requests from one app instance to another. At
4
- [Springest](http://www.springest.com) we use this to test new features.
5
- We replicate all live requests to our staging environment to test new
6
- code before it goes live. With real traffic!
4
+ [Springest](http://www.springest.com) we used
5
+ [Gor](https://github.com/buger/gor) once to test our new Postgres stack
6
+ vs our at that time current MySQL stack.
7
+ We replicated all live requests to our staging environment to test new
8
+ code before it went live. With real traffic!
7
9
 
8
- ## Session support
10
+ Unfortunately, we could not test everything we wanted with the
11
+ [setup with Gor](http://devblog.springest.com/testing-big-infrastructure-changes-at-springest/).
12
+ Stuff that relied on sessions (like
13
+ [MySpringest](https://www.springest.com/my-springest), and course
14
+ management through our [Admin panel](http://providers.springest.com/))
15
+ could not be tested properly because the staging environment did not
16
+ share sessions with the production stack.
17
+
18
+ Recently, @foxycoder was asked to give a talk about these adventures at
19
+ [Amsterdam.rb](http://www.meetup.com/Amsterdam-rb/events/206133762/).
20
+ And while we were thinking about all the stuff that we needed to do to
21
+ get it right with Gor, we came up with the concept of this gem.
22
+
23
+ ## Full control over the requests through Rack
24
+
25
+ This is Rack MiddleWare. And thanks to that, we have all the information
26
+ and handy tools available to parse and alter request data before we
27
+ forward it to another stack.
28
+
29
+
30
+ ## Features
31
+
32
+ At the moment, it just forwards the request with only a couple of
33
+ changes:
34
+
35
+ - The host and/or port to match the stack to forward to.
36
+ - The session cookie – it stores a persistent link between the source
37
+ app's session and the destination app's session in Redis.
38
+ - The CSRF token – same as the session, the destination app's
39
+ `authenticity_token` is persistent and used in consecutive requests.
40
+
41
+ ### Session support
9
42
 
10
43
  It has support for sessions. To make use of it, you need to have Redis
11
44
  running. Redis serves as a key-value store where sessions from the
12
45
  Source App are linked to sessions from the Forward App. This way both
13
46
  apps can have their own session management.
14
47
 
48
+ ### Rails's CSRF tokens
49
+
50
+ Rails uses a cross site scripting defense mechanism in the form of an
51
+ `authenticity_token` parameter. Absense of it, or modifying it between
52
+ requests results in XSS errors. Therefore we needed to make sure these
53
+ were properly captured and replaced by the Forwarder before sending the
54
+ request to the other application.
55
+
56
+ CSRF tokens are also persisted in Redis and used in consecutive requests, by
57
+ updating `params["authenticity_token"]` before handing off the request
58
+ to the replica app.
59
+
15
60
  ## API Docs
16
61
 
17
62
  Check out the official [API docs](http://rubydoc.info/gems/rack-request_replication)
@@ -1,3 +1,2 @@
1
1
  require "rack/request_replication/version"
2
- require "rack/request_replication/logger"
3
2
  require "rack/request_replication/forwarder"
@@ -67,13 +67,68 @@ module Rack
67
67
  Thread.new do
68
68
  begin
69
69
  forward_request.add_field("Cookie", cookies( request ))
70
- update_cookies( request, http.request(forward_request) )
70
+ update_csrf_token_and_cookies( request, http.request(forward_request) )
71
71
  rescue => e
72
72
  logger.debug "Replicating request failed with: #{e.message}"
73
73
  end
74
74
  end
75
75
  end
76
76
 
77
+ ##
78
+ # Update CSRF token and cookies.
79
+ #
80
+ # @param [Rack::Request] request
81
+ # @param [Net::HTTP::Response] response
82
+ #
83
+ def update_csrf_token_and_cookies( request, response )
84
+ update_csrf_token( request, response )
85
+ update_cookies( request, response )
86
+ end
87
+
88
+ ##
89
+ # The CSRF-token to use.
90
+ #
91
+ # @param [Rack::Request] request
92
+ # @returns [String]
93
+ #
94
+ def csrf_token( request )
95
+ token = request.params["authenticity_token"]
96
+ return if token.nil?
97
+
98
+ redis.get( "csrf-#{token}" ) || token
99
+ end
100
+
101
+ ##
102
+ # Update CSRF token to bypass XSS errors in Rails.
103
+ #
104
+ # @param [Rack::Request] request
105
+ #
106
+ def update_csrf_token( request, response )
107
+ token = request.params["authenticity_token"]
108
+ return if token.nil?
109
+
110
+ response_token = csrf_token_from response
111
+ return token if response_token.nil?
112
+
113
+ redis.set "csrf-#{token}", response_token
114
+ end
115
+
116
+ ##
117
+ # Pull CSRF token from the HTML document's header.
118
+ #
119
+ # @param [Net::HTTP::Response] response
120
+ # @returns [String]
121
+ #
122
+ def csrf_token_from( response )
123
+ response.split("\n").
124
+ select{|l| l.match(/csrf-token/) }.
125
+ first.split(" ").
126
+ select{|t| t.match(/^content=/)}.first.
127
+ match(/content="(.*)"/)[1]
128
+ rescue
129
+ nil
130
+ end
131
+
77
132
  ##
78
133
  # Update cookies from the forwarded request using the session id
79
134
  # from the cookie of the source app as a key. The cookie is stored
@@ -234,6 +289,11 @@ module Rack
234
289
  value = request.send( m )
235
290
  replicated_options[m] = value unless value.nil?
236
291
  end
292
+
293
+ if replicated_options[:params]["authenticity_token"]
294
+ replicated_options[:params]["authenticity_token"] = csrf_token
295
+ end
296
+
237
297
  replicated_options
238
298
  end
239
299
 
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  module RequestReplication
3
- VERSION = "0.0.3"
3
+ VERSION = "0.0.5"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-request_replication
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter de Vos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-15 00:00:00.000000000 Z
11
+ date: 2014-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler