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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 70ee014ab99f99fa2f2ad204ce13a2bd7962a7ad
4
- data.tar.gz: ef49f9684ecaf820f6f8d0d0786d4c2aa0192f7c
3
+ metadata.gz: a9b92685bdfb1562d5c91f2f43e7d9f011bd7175
4
+ data.tar.gz: 4039479856583c1db25262e525da2e18ae1846c3
5
5
  SHA512:
6
- metadata.gz: 069c8f5fe4346c9abf868ef3ba77c770fe34b6cd067e80f281fe8cac52a432de106119ac252fb47de0184a4792a9ed824d2b6ab340ed2da2aeeadf1715263aaf
7
- data.tar.gz: aa3860206e132799bad764c79cd029f4f1af5ef4403bccf0c9d3762cba2be65630025f5d8cdce8302d490252d6663f1662bab9bf0ab4905f0b0d639a2b6cb51d
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 module for the Rack Session Manipulation middleware.
1
+ # Parent namespace for the Rack Session Manipulation middleware.
2
2
  module RackSessionManipulation
3
- # Capybara helpers for accessing and setting session values
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
- def session=(hash)
6
- data = { 'session_data' => RackSessionManipulation.encode(hash) }
7
- driver.put(RackSessionManipulation.config.path, data)
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 module for the Rack Session Manipulation middleware.
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 module for the Rack Session Manipulation middleware.
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
- def initialize(app, _options = {})
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
- 'GET' => :retrieve,
17
- 'PUT' => :update
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-Type' => 'application/json',
37
- 'Content-Length' => length
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
- [204, headers(0), '']
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 module for the Rack Session Manipulation middleware.
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**64 - 1)
34
+ SecureRandom.random_number(2**128 - 1)
23
35
  rescue LoadError, NotImplementedError
24
- Kernel.rand(2**64 - 1)
36
+ Kernel.rand(2**128 - 1)
25
37
  end
26
- format('/%016x', num)
38
+ format('/%032x', num)
27
39
  end
28
40
  end
29
41
  end
@@ -1,5 +1,5 @@
1
- # Parent module for the Rack Session Manipulation middleware.
1
+ # Parent namespace for the Rack Session Manipulation middleware.
2
2
  module RackSessionManipulation
3
3
  # The current version of this module
4
- VERSION = '0.1.0'
4
+ VERSION = '0.7.0'
5
5
  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
- # Parent module for the Rack Session Manipulation middleware.
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.1.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-03-28 00:00:00.000000000 Z
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.4.3
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.