rack_session_manipulation 0.1.0 → 0.7.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/README.md +18 -0
- data/lib/rack_session_manipulation/capybara.rb +52 -5
- data/lib/rack_session_manipulation/config.rb +2 -1
- data/lib/rack_session_manipulation/middleware.rb +70 -8
- data/lib/rack_session_manipulation/utilities.rb +16 -4
- data/lib/rack_session_manipulation/version.rb +2 -2
- data/lib/rack_session_manipulation.rb +10 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9b92685bdfb1562d5c91f2f43e7d9f011bd7175
|
4
|
+
data.tar.gz: 4039479856583c1db25262e525da2e18ae1846c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35eb5e1f919e08e8f0182b7e996e096a811f6f9ca82df0a7013efa43eab50189d36089c7288b0bd5c6c878c82c7bafa26a118aa9054af74d1376eedb7ffda298
|
7
|
+
data.tar.gz: ec6dd5bcfb8be8733b156d77ac76bb873a70bd5c484f272c7fd4f7a80fc8b15ab4824d237d57aed88c4635a91a6a4d51d330198776e92eda2c855f7126fb6f04
|
data/README.md
CHANGED
@@ -1,9 +1,27 @@
|
|
1
1
|
# RackSessionManipulation
|
2
2
|
|
3
|
+
[](https://rubygems.org/gems/rack_session_manipulation)
|
3
4
|
[](https://travis-ci.org/sstelfox/rack_session_manipulation)
|
4
5
|
[](https://coveralls.io/r/sstelfox/rack_session_manipulation?branch=develop)
|
5
6
|
[](https://gemnasium.com/sstelfox/rack_session_manipulation)
|
6
7
|
[](https://codeclimate.com/github/sstelfox/rack_session_manipulation)
|
8
|
+
[](http://www.rubydoc.info/gems/rack_session_manipulation)
|
9
|
+
|
10
|
+
RackSessionManipulation is rack middleware designed to expose internal session
|
11
|
+
information to be read and modified from within tests. This is intended to
|
12
|
+
assist in treating routes as modular units of work that can be tested in
|
13
|
+
isolation without the dependency of the rest of your application.
|
14
|
+
|
15
|
+
This is not intended to replace full set of 'behavior' tests, but can be used
|
16
|
+
to speed up getting to a 'Given' state in place of helpers that previously
|
17
|
+
would be forced to walk through your entire application stack to establish the
|
18
|
+
target state. That walkthrough is valuable but can be redundant and unecessary.
|
19
|
+
|
20
|
+
This middleware should never be used in production as it allows arbitrary
|
21
|
+
tampering of encrypted server-side session information without any form of
|
22
|
+
authentication. Use in production can lead to abuses such as user
|
23
|
+
impersonation, privilege escalation, and private information exposure depending
|
24
|
+
on what the application stores in the session.
|
7
25
|
|
8
26
|
## Installation
|
9
27
|
|
@@ -1,18 +1,65 @@
|
|
1
|
-
# Parent
|
1
|
+
# Parent namespace for the Rack Session Manipulation middleware.
|
2
2
|
module RackSessionManipulation
|
3
|
-
#
|
3
|
+
# This module exposes the session state helpers to Capybara, allowing feature
|
4
|
+
# tests to quickly access the session mechanisms.
|
4
5
|
module Capybara
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
# Provides a mechanism to completely reset the server's session to a fresh
|
7
|
+
# blank slate.
|
8
|
+
def reset_session
|
9
|
+
driver_method_fallback(:delete, RackSessionManipulation.config.path, data)
|
8
10
|
end
|
9
11
|
|
12
|
+
# Retrieve a hash containing the entire state of the current session.
|
13
|
+
#
|
14
|
+
# @return [Hash] Hash of the session
|
10
15
|
def session
|
11
16
|
driver.get(RackSessionManipulation.config.path)
|
12
17
|
RackSessionManipulation.decode(driver.response.body)
|
13
18
|
end
|
19
|
+
|
20
|
+
# Updates the state of the current session. An important thing to note
|
21
|
+
# about this mechanism is that it does not set the session to perfectly
|
22
|
+
# match the state of the provided hash. The provided hash is effectively
|
23
|
+
# merged with the current state of the session.
|
24
|
+
#
|
25
|
+
# This also does not support marshalling objects into the session state.
|
26
|
+
# This limitation is intentional as objects stored in sessions tend to be
|
27
|
+
# a detriment to both performance and security.
|
28
|
+
#
|
29
|
+
# @param [Hash] hash Data to be set / updated within the current session.
|
30
|
+
def session=(hash)
|
31
|
+
data = { 'session_data' => RackSessionManipulation.encode(hash) }
|
32
|
+
driver_method_fallback(:put, RackSessionManipulation.config.path, data)
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
# Capybara drivers are only required to implement the #get method. Where
|
38
|
+
# appropriate we'll use the correct HTTP method, but when unavailable we'll
|
39
|
+
# use the fallback mechanism.
|
40
|
+
#
|
41
|
+
# There may be a subtle bug in this fallback mechanism when the driver
|
42
|
+
# doesn't support 'POST' requests. If large data payloads are used. Normal
|
43
|
+
# HTTP request parameters within the URL itself are limited to 1024
|
44
|
+
# characters.
|
45
|
+
#
|
46
|
+
# @param [Symbol] method A valid HTTP method name (all lowercase).
|
47
|
+
# @param [String] path The path to request in the driver's rack app.
|
48
|
+
# @param [Hash] dat An optional hash of data that will be sent along as
|
49
|
+
# part of the query parameters.
|
50
|
+
def driver_method_fallback(method, path, dat = {})
|
51
|
+
if driver.respond_to?(method)
|
52
|
+
driver.send(method, path, dat)
|
53
|
+
return
|
54
|
+
end
|
55
|
+
|
56
|
+
data.merge!('_method' => method.to_s.upcase)
|
57
|
+
driver.respond_to?(:post) ? driver.post(path, dat) : driver.get(path, dat)
|
58
|
+
end
|
14
59
|
end
|
15
60
|
end
|
16
61
|
|
62
|
+
# When this file is required automatically expose it through the Capybara
|
63
|
+
# session helpers.
|
17
64
|
require 'capybara/session'
|
18
65
|
Capybara::Session.send(:include, RackSessionManipulation::Capybara)
|
@@ -1,4 +1,5 @@
|
|
1
|
-
# Parent
|
1
|
+
# Parent namespace for the Rack Session Manipulation middleware.
|
2
2
|
module RackSessionManipulation
|
3
|
+
# A data storage structure for holding configuration related information.
|
3
4
|
Config = Struct.new(:path)
|
4
5
|
end
|
@@ -1,8 +1,17 @@
|
|
1
|
-
# Parent
|
1
|
+
# Parent namespace for the Rack Session Manipulation middleware.
|
2
2
|
module RackSessionManipulation
|
3
3
|
# Rack middleware that handles the accessing and modification of session
|
4
4
|
# state.
|
5
5
|
class Middleware
|
6
|
+
# Primary entry point of this middleware. Every request that makes it this
|
7
|
+
# far into the stack will be parsed and when it matches something this
|
8
|
+
# middleware is designed to handle it will stop the chain and process it
|
9
|
+
# appropriately.
|
10
|
+
#
|
11
|
+
# @param [Hash] env An environment hash containing everything about the
|
12
|
+
# client's request.
|
13
|
+
# @return [Array<Fixnum, Hash, String>] A generated response to the
|
14
|
+
# client's request.
|
6
15
|
def call(env)
|
7
16
|
request = Rack::Request.new(env)
|
8
17
|
|
@@ -10,34 +19,80 @@ module RackSessionManipulation
|
|
10
19
|
action.nil? ? @app.call(env) : send(action, request)
|
11
20
|
end
|
12
21
|
|
13
|
-
|
22
|
+
# Setup the middleware with the primary application passed in, anything we
|
23
|
+
# can't handle will be passed to this application.
|
24
|
+
#
|
25
|
+
# @param [Object#call] app A rack application that implements the #call
|
26
|
+
# method.
|
27
|
+
def initialize(app)
|
14
28
|
@app = app
|
15
29
|
@routes = {
|
16
|
-
'
|
17
|
-
'
|
30
|
+
'DELETE' => :reset,
|
31
|
+
'GET' => :retrieve,
|
32
|
+
'PUT' => :update
|
18
33
|
}
|
19
34
|
end
|
20
35
|
|
21
36
|
protected
|
22
37
|
|
38
|
+
# Look up what HTTP method was used for this request. In the event the
|
39
|
+
# client doesn't support all HTTP methods, the standard '_method' parameter
|
40
|
+
# fall back is made available to override the method actually used.
|
41
|
+
#
|
42
|
+
# Normally the '_method' fall back is only accepted over the POST method,
|
43
|
+
# however, Capybara drivers are only required to implement GET method and
|
44
|
+
# so this does not require any particular method to support the override.
|
45
|
+
#
|
46
|
+
# When a GET method was used to provide data, a subtle issue can crop up.
|
47
|
+
# URLs can't exceed 1024 characters. Generally this results in them being
|
48
|
+
# truncated which in turn will cause a JSON parse error and no changes
|
49
|
+
# being made to the session.
|
50
|
+
#
|
51
|
+
# @param [Rack::Request] request The request to analyze
|
52
|
+
# @return [String] The decided on HTTP method
|
23
53
|
def extract_method(request)
|
24
|
-
return request.request_method unless request.request_method == 'POST'
|
25
54
|
return request.params['_method'].upcase if request.params['_method']
|
26
55
|
request.request_method
|
27
56
|
end
|
28
57
|
|
58
|
+
# Considers the request and if it matches something this middleware handles
|
59
|
+
# return the symbol matching the name of the method that should handle the
|
60
|
+
# request.
|
61
|
+
#
|
62
|
+
# @param [Rack::Request] request The request to assess
|
63
|
+
# @return [Symbol,Nil] Name of method to use or nil if this middleware
|
64
|
+
# should pass the request on to the app.
|
29
65
|
def get_action(request)
|
30
66
|
return unless request.path == RackSessionManipulation.config.path
|
31
67
|
@routes[extract_method(request)]
|
32
68
|
end
|
33
69
|
|
70
|
+
# A helper mechanism to consistently generate common headers client will
|
71
|
+
# expect.
|
72
|
+
#
|
73
|
+
# @param [Fixnum] length Byte size of the message body.
|
74
|
+
# @return [Hash] The common headers
|
34
75
|
def headers(length)
|
35
76
|
{
|
36
|
-
'Content-
|
37
|
-
'Content-
|
77
|
+
'Content-Length' => length,
|
78
|
+
'Content-Type' => 'application/json; charset=utf-8'
|
38
79
|
}
|
39
80
|
end
|
40
81
|
|
82
|
+
# Handle requests to entirely reset the session state.
|
83
|
+
#
|
84
|
+
# @param [Rack::Request] request
|
85
|
+
# @return [Array<Fixnum, Hash, String>]
|
86
|
+
def reset(request)
|
87
|
+
request.env['rack.session'].clear
|
88
|
+
[204, headers(0), '']
|
89
|
+
end
|
90
|
+
|
91
|
+
# Retrieve the entire contents of the session and properly encode it
|
92
|
+
# before returning.
|
93
|
+
#
|
94
|
+
# @param [Rack::Request] request
|
95
|
+
# @return [Array<Fixnum, Hash, String>]
|
41
96
|
def retrieve(request)
|
42
97
|
session_hash = request.env['rack.session'].to_hash
|
43
98
|
content = RackSessionManipulation.encode(session_hash)
|
@@ -45,13 +100,20 @@ module RackSessionManipulation
|
|
45
100
|
[200, headers(content.length), content]
|
46
101
|
end
|
47
102
|
|
103
|
+
# Update the current state of the session with the provided data. This
|
104
|
+
# works effectively like a hash merge on the current session only setting
|
105
|
+
# and overriding keys in the session data provided.
|
106
|
+
#
|
107
|
+
# @param [Rack::Request] request
|
108
|
+
# @return [Array<Fixnum, Hash, String>]
|
48
109
|
def update(request)
|
49
110
|
session_data = request.params['session_data']
|
50
111
|
RackSessionManipulation.decode(session_data).each do |k, v|
|
51
112
|
request.env['rack.session'][k] = v
|
52
113
|
end
|
53
114
|
|
54
|
-
|
115
|
+
loc_hdr = { 'Location' => RackSessionManipulation.config.path }
|
116
|
+
[303, headers(0).merge(loc_hdr), '']
|
55
117
|
rescue JSON::ParserError
|
56
118
|
[400, headers(0), '']
|
57
119
|
end
|
@@ -1,12 +1,24 @@
|
|
1
|
-
# Parent
|
1
|
+
# Parent namespace for the Rack Session Manipulation middleware.
|
2
2
|
module RackSessionManipulation
|
3
3
|
# Various utility methods that will be module level functions for the parent
|
4
4
|
# namespace module.
|
5
5
|
module Utilities
|
6
|
+
# Helper method for decoding the session state into a standard Ruby hash.
|
7
|
+
# This data is exposed as JSON, and is parsed as such.
|
8
|
+
#
|
9
|
+
# @param [String] encoded_data JSON encoded session data
|
10
|
+
# @return [Hash] Hash version of the session data
|
6
11
|
def decode(encoded_data)
|
7
12
|
JSON.parse(encoded_data)
|
8
13
|
end
|
9
14
|
|
15
|
+
# Helper method for encoding modified session state for the middleware. The
|
16
|
+
# middleware expects JSON so this is just a thin wrapper for encoding to
|
17
|
+
# JSON.
|
18
|
+
#
|
19
|
+
# @param [Hash] obj An object that can be serialized using JSON, generally
|
20
|
+
# this is a hash.
|
21
|
+
# @return [String] The encoded data.
|
10
22
|
def encode(obj)
|
11
23
|
JSON.generate(obj)
|
12
24
|
end
|
@@ -19,11 +31,11 @@ module RackSessionManipulation
|
|
19
31
|
def random_path_prefix
|
20
32
|
num = begin
|
21
33
|
require 'securerandom'
|
22
|
-
SecureRandom.random_number(2**
|
34
|
+
SecureRandom.random_number(2**128 - 1)
|
23
35
|
rescue LoadError, NotImplementedError
|
24
|
-
Kernel.rand(2**
|
36
|
+
Kernel.rand(2**128 - 1)
|
25
37
|
end
|
26
|
-
format('/%
|
38
|
+
format('/%032x', num)
|
27
39
|
end
|
28
40
|
end
|
29
41
|
end
|
@@ -5,14 +5,23 @@ require 'rack_session_manipulation/middleware'
|
|
5
5
|
require 'rack_session_manipulation/utilities'
|
6
6
|
require 'rack_session_manipulation/version'
|
7
7
|
|
8
|
-
#
|
8
|
+
# RackSessionManipulation is rack middleware designed to expose internal
|
9
|
+
# session information to be read and modified from within tests.
|
9
10
|
module RackSessionManipulation
|
10
11
|
extend RackSessionManipulation::Utilities
|
11
12
|
|
13
|
+
# Keeps a globally accessible instance of a configuration object. This will
|
14
|
+
# initialize a new Config object the first time this is called (using a
|
15
|
+
# random path for the session access path).
|
16
|
+
#
|
17
|
+
# @return [RackSessionManipulation::Config]
|
12
18
|
def self.config
|
13
19
|
@config ||= RackSessionManipulation::Config.new(random_path_prefix)
|
14
20
|
end
|
15
21
|
|
22
|
+
# Allows block DSL style configuration of the global configuration instance.
|
23
|
+
#
|
24
|
+
# @yield [RackSessionManipulation::Config]
|
16
25
|
def self.configure
|
17
26
|
yield(config)
|
18
27
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack_session_manipulation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Stelfox
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -222,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
222
|
version: '0'
|
223
223
|
requirements: []
|
224
224
|
rubyforge_project:
|
225
|
-
rubygems_version: 2.
|
225
|
+
rubygems_version: 2.2.2
|
226
226
|
signing_key:
|
227
227
|
specification_version: 4
|
228
228
|
summary: A rack middleware for exposing session state to tests.
|