rack_session_manipulation 0.1.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/rack_session_manipulation.svg)](https://rubygems.org/gems/rack_session_manipulation)
|
3
4
|
[![Build Status](https://travis-ci.org/sstelfox/rack_session_manipulation.svg)](https://travis-ci.org/sstelfox/rack_session_manipulation)
|
4
5
|
[![Coverage Status](https://coveralls.io/repos/sstelfox/rack_session_manipulation/badge.svg?branch=develop)](https://coveralls.io/r/sstelfox/rack_session_manipulation?branch=develop)
|
5
6
|
[![Dependency Status](https://gemnasium.com/sstelfox/rack_session_manipulation.svg)](https://gemnasium.com/sstelfox/rack_session_manipulation)
|
6
7
|
[![Code Climate](https://codeclimate.com/github/sstelfox/rack_session_manipulation/badges/gpa.svg)](https://codeclimate.com/github/sstelfox/rack_session_manipulation)
|
8
|
+
[![Yard Docs](http://img.shields.io/badge/yard-docs-green.svg)](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.
|