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 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.