simple_session 0.1.1 → 0.2.2

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: de9e37a31c667a04a6812dd26ae80c4627da92f1
4
- data.tar.gz: f12ea3605069c9e6558d39aff93247074cf98653
3
+ metadata.gz: 22b41938246e413705405ae2f83ebb0104761677
4
+ data.tar.gz: cc6b51706663df70fb814a27e74b12babd7ee0b4
5
5
  SHA512:
6
- metadata.gz: 64e8397ac79a60536cf616b9d1fec65131bfdabc0888b443ecc5e34ac4ea5a759509cd21ec06490783e62f3891430aaee31203892294756b49125662ce493c2a
7
- data.tar.gz: a871554c7cd6ac5d9a0e7eb0ebbf6e59fe7552ef6f19be42858ad0a03a64fa0cb8fd32c7b0fd021d682629e10ed2c8efea076f49e23de72e41e603c0e9a1a3d1
6
+ metadata.gz: 75250ae913ae857b5b9e4375bbff4b851d246f2b2cc8f810627c3c0f587d72ac9c7b396af6a810f531d7c10d05bf98993a147875fab3d1a48e5e2eef524bc1f0
7
+ data.tar.gz: a0a1dbedc75f826d0eda5077833b2f9986cf94a4f058702a626d1924ef36b4c512335b28d1df0d667d153aebeea334c0bc8fc202b6153cc4689ac73efa4ec4ff
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ .byebug_history
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # SimpleSession
2
+ ![build-status](https://travis-ci.org/hayduke19us/simple_session.svg?branch=master)
2
3
 
3
4
  This is a drop in replacement for rack session. By default
4
5
  the session cookie is encrypted in AES-256-CBC and requires a secret
@@ -8,10 +9,9 @@ which is recommended to be kept in an .env file or something similar.
8
9
 
9
10
  <a href='#usage-sect'><h4>Usage</h4></a>
10
11
 
11
- <a href='#overview-sect'><h4>Overview</h4></a>
12
-
13
12
  <a href='#default-sect'><h4>Default Options</h4></a>
14
13
 
14
+ <a href='#overview-sect'><h4>Overview</h4></a>
15
15
 
16
16
  <h2 id='install-sect'>Installation</h2>
17
17
 
@@ -34,71 +34,78 @@ Full examples are in the *test/simple_session_test.rb* and
34
34
  *test/simple_app.rb*. It's just a middleware so throw it on top of the stack.
35
35
 
36
36
  ```ruby
37
- use SimpleSession::Session, secret: 'some secret', expire_after: 7200
37
+ use SimpleSession::Session, secret: SecureRandom.hex
38
+ ```
39
+ **NOTE:** `:secret` must be 32 chars long.
40
+
41
+ <h4 id='default-sect'>Default Options</h4>
42
+
43
+ ```ruby
44
+ secret: nil
45
+ key: 'rack.session',
46
+ options_key: 'rack.session.options' ,
47
+ max_age: 172800,
48
+ path: '/',
49
+ domain: 'nil',
50
+ secure: false,
51
+ http_only: false
52
+ ```
53
+ **NOTE:** For persistent options `:max_age` is excepted and the default is 2 days.
54
+ Because there are still IE versions that don't support max-age we inject both **max-age** and **expires** into the cookie and let the browser handle it.
55
+
56
+ The following is a simple example. The only **required argument is :secret**.
57
+
58
+ ```ruby
59
+ require 'sinatra'
60
+ require 'simple_session'
61
+
62
+ class SimpleApp < Sinatra::Base
63
+
64
+ SECRET = SecureRandom.hex
65
+ use SimpleSession::Session, secret: SECRET
66
+
67
+ get '/signin' do
68
+ if session[:user_id]
69
+ "Already Signed in"
70
+ else
71
+ session[:user_id] = '!Green3ggsandHam!'
72
+ "Id: #{ session[:user_id] }"
73
+ end
74
+ end
75
+
76
+ end
38
77
  ```
39
78
 
40
79
  <h4 id='overview-sect'>Overview</h4>
41
80
  SimpleSession is a simple Middleware that processes the session cookie
42
- with 5 steps.
81
+ with 4 steps.
43
82
 
44
- 1. Extract the session from the request if there is one. If there is no session
83
+ * Extract the session from the request if there is one. If there is no session
45
84
  create a new one that looks like this:
46
85
 
47
86
  ```ruby
48
87
  { session_id: 'some secret id' }
49
88
  ```
50
-
51
- 2. Load the session data into the app environment so they are accessible with racks request methods like this:
52
-
89
+ * Load the session data into the app environment so they are accessible with racks request methods like this:
53
90
  ```ruby
54
91
  get '/'
55
- request.session
92
+ request.session
56
93
  session
57
94
  request.session_options
58
95
  end
59
96
  ```
60
-
61
- 3. Clear the session if the time has expired and create a new one.
62
-
63
- 4. Update the options if they have been changed like this.
97
+ * Update the options if they have been changed like this.
64
98
 
65
99
  ```ruby
66
100
  # This changes the session to expire one minute after
67
101
  # the current time.
68
102
  get '/'
69
- request.session_options[:expire_after] = 60
103
+ request.session_options[:max_age] = 60
70
104
  end
71
105
  ```
72
106
 
73
- 5. Create the new session cookie, encrypt it and return the response.
74
-
75
- <h4 id='default-sect'>Default Options</h4>
107
+ * Create the new session cookie, encrypt it and return the response.
76
108
 
77
- * secret: nil
78
- * key: 'rack.session'
79
- * expire_after: 7200
80
-
81
- The following is a simple example. The only **required argument is :secret**.
82
-
83
- ```ruby
84
- require 'sinatra'
85
- require 'simple_session'
86
-
87
- class SimpleApp < Sinatra::Base
88
-
89
- use SimpleSession::Session, secret: 'Your Secret'
90
-
91
- get '/signin' do
92
- if session[:user_id]
93
- "Already Signed in"
94
- else
95
- session[:user_id] = '!Green3ggsandHam!'
96
- "Id: #{ session[:user_id] }"
97
- end
98
- end
99
-
100
- end
101
- ```
102
109
 
103
110
  ## Development
104
111
 
@@ -1,7 +1,6 @@
1
1
  require 'rack/request'
2
2
  require 'securerandom'
3
3
  require 'openssl'
4
- require 'byebug'
5
4
 
6
5
  module SimpleSession
7
6
 
@@ -12,10 +11,25 @@ module SimpleSession
12
11
  def initialize app, options = {}
13
12
  @app = app
14
13
  @key = options.fetch :key, 'rack.session'
15
- @secret = options[:secret]
16
- @cipher_key = cipher_key
14
+ @secret = options.fetch :secret, get_secret_errors(options)
17
15
  @options_key = options.fetch :options_key, 'rack.session.options'
18
- @default_opts = options
16
+ @default_opts = DEFAULT_OPTS.merge!(options)
17
+ end
18
+
19
+ def get_secret_errors options
20
+ secret = options[:secret]
21
+ missing_msg = %[
22
+ SimpleSession requires a secret like this:
23
+ use SimpleSession::Session, secret: 'some secret'
24
+ ]
25
+
26
+ short_msg = %[
27
+ SimpleSession require a secret with a minimum length of 32
28
+ use SimpleSession::Session, secret: SecureRandom.hex(32)
29
+ ]
30
+
31
+ raise ArgumentError, missing_msg unless secret
32
+ raise ArgumentError, short_msg unless secret.length >= 32
19
33
  end
20
34
 
21
35
  def req_session
@@ -26,15 +40,6 @@ module SimpleSession
26
40
  session[:options] if session
27
41
  end
28
42
 
29
- def clear_session
30
- @session = new_session_hash
31
- @options = {options: OptionHash.new(@default_opts).opts}
32
- end
33
-
34
- def time_expired?
35
- req_options && req_options[:expire] && req_options[:expire] <= Time.now
36
- end
37
-
38
43
  def new_session_hash
39
44
  { session_id: SecureRandom.hex(32) }
40
45
  end
@@ -43,19 +48,15 @@ module SimpleSession
43
48
  # Decrypt request session and store it
44
49
  extract_session env
45
50
 
46
- original_opts = @options[:options].dup
47
-
48
- # Process options
49
- clear_session if time_expired?
50
-
51
51
  # Load session into app env
52
52
  load_environment env
53
53
 
54
54
  # Pass on request
55
55
  status, headers, body = @app.call env
56
56
 
57
- # Check session for changes and update options
58
- update_options if options_changed? original_opts
57
+ # Check session for changes and update
58
+ update_options if options_changed?
59
+ update_session if session_changed?
59
60
 
60
61
  # Encrypt and add session to headers
61
62
  add_session headers
@@ -64,27 +65,12 @@ module SimpleSession
64
65
  end
65
66
 
66
67
  private
67
-
68
- def update_options
69
- @options = {options: OptionHash.new(@options[:options]).opts}
70
- end
71
-
72
- def options_changed? original
73
- original != @options[:options]
74
- end
75
-
76
- # If the session is nil create a new one
77
- # If the session is unable to be decrypted throw an
78
- # error and create a new one.
79
- # encrypted data is not allowed in the app env
80
68
  def extract_session env
81
69
  begin
82
70
  @request = Rack::Request.new env
83
71
  @session = req_session ? decrypt(req_session) : new_session_hash
84
72
 
85
- unless @session
86
- raise ArgumentError, "Unable to decrypt session #{caller[0]}"
87
- end
73
+ raise ArgumentError, "Unable to decrypt session" unless @session
88
74
 
89
75
  rescue => e
90
76
  @session = new_session_hash
@@ -94,63 +80,64 @@ module SimpleSession
94
80
  @options = options_hash
95
81
  end
96
82
 
83
+ def options_hash
84
+ o = session[:options] || OptionHash.new(@default_opts).opts
85
+ { options: o }
86
+ end
87
+
97
88
  def load_environment env
98
- env[@key] = session[:value] || session
99
- env[@options_key] = @options[:options]
89
+ env[@key] = session.dup
90
+ env[@options_key] = @options[:options].dup
100
91
  end
101
92
 
102
93
  def add_session headers
103
94
  cookie = Hash.new
104
- cookie[:value] = session[:value] || session
105
- cookie[:expires] = @options[:options][:expire]
106
- set_cookie_header headers, @key, encrypt(cookie.merge!(@options))
95
+ cookie[:value] = encrypt session.merge(@options)
96
+ cookie.merge!(@options[:options])
97
+
98
+ set_cookie_header headers, @key, cookie
107
99
  end
108
100
 
109
101
  def set_cookie_header headers, key, cookie
110
102
  Rack::Utils.set_cookie_header! headers, key, cookie
111
103
  end
112
104
 
113
- def options_hash
114
- o = session[:options] || OptionHash.new(@default_opts).opts
115
- { options: o }
105
+ def update_options
106
+ @options = {options: OptionHash.new(request.session_options).opts}
107
+ end
108
+
109
+ def options_changed?
110
+ request.session_options != @options[:options]
111
+ end
112
+
113
+ def session_changed?
114
+ request.session != @session
115
+ end
116
+
117
+ def update_session
118
+ @session = request.session
116
119
  end
117
120
 
118
121
  def encrypt data
119
- begin
120
- if data.is_a? Hash
121
- # Serialize
122
- m = Marshal.dump data
122
+ # Serialize
123
+ m = Marshal.dump data
123
124
 
124
- # Cipher
125
- c = load_cipher m
125
+ # Cipher
126
+ c = load_cipher m
126
127
 
127
- # Base64
128
- [c].pack('m')
129
- else
130
- raise ArgumentError, "Internal session data must be a hash"
131
- end
132
- rescue => e
133
- puts e.message
134
- end
128
+ # Encode Base64
129
+ [c].pack('m')
135
130
  end
136
131
 
137
132
  def decrypt data
138
- begin
139
- if data.is_a? String
140
- # Decode Base64
141
- b = data.unpack('m').first
133
+ # Decode Base64
134
+ b = data.unpack('m').first
142
135
 
143
- # Decipher
144
- c = unload_cipher b
136
+ # Decipher
137
+ c = unload_cipher b
145
138
 
146
- # Deserialize
147
- Marshal.load c
148
- else
149
- raise ArgumentError, "External session data must be a string."
150
- end
151
- rescue => e
152
- puts e.message
153
- end
139
+ # Deserialize
140
+ Marshal.load c
154
141
  end
155
142
 
156
143
  def digest
@@ -158,7 +145,7 @@ module SimpleSession
158
145
  end
159
146
 
160
147
  def hmac data
161
- OpenSSL::HMAC.digest digest, @key, data
148
+ OpenSSL::HMAC.digest digest, @secret, data
162
149
  end
163
150
 
164
151
  def new_cipher
@@ -170,7 +157,7 @@ module SimpleSession
170
157
  cipher.encrypt
171
158
  iv = cipher.random_iv
172
159
  cipher.iv = iv
173
- cipher.key = @cipher_key
160
+ cipher.key = cipher_key
174
161
 
175
162
  iv + cipher.update(marshaled_data) + cipher.final
176
163
  end
@@ -179,7 +166,7 @@ module SimpleSession
179
166
  cipher = new_cipher
180
167
  cipher.decrypt
181
168
  cipher.iv = data[0, 16]
182
- cipher.key = @cipher_key
169
+ cipher.key = cipher_key
183
170
  cipher.update(data[16..-1]) + cipher.final
184
171
  end
185
172
 
@@ -192,28 +179,17 @@ module SimpleSession
192
179
  SANITATION = [:key, :secret, :options_key]
193
180
 
194
181
  def initialize args
195
- @opts = sanitize args.dup
182
+ @opts = args
196
183
  process_request_options
197
- set_instance_variables
198
- create_readers
184
+ sanitize_opts
199
185
  end
200
186
 
201
187
  def opts
202
188
  @opts
203
189
  end
204
190
 
205
- def create_reader name
206
- self.class.send(:define_method, name, &Proc.new)
207
- end
208
-
209
- def create_readers
210
- @opts.keys.each do |k|
211
- create_reader(k) { instance_variable_get "@#{k}"}
212
- end
213
- end
214
-
215
- def set_instance_variables
216
- @opts.each { |k, v| instance_variable_set "@#{k.to_s}", v}
191
+ def sanitize_opts
192
+ @opts = sanitize @opts
217
193
  end
218
194
 
219
195
  def process_request_options
@@ -225,26 +201,18 @@ module SimpleSession
225
201
  end
226
202
 
227
203
  def p_time
228
- @opts[:expire_after] ||= default_time
229
- @opts[:expire] = Time.now + @opts[:expire_after].to_i if @opts[:expire_after]
204
+ time = Time.now + @opts[:max_age].to_i
205
+ @opts[:expires] = time if @opts[:max_age]
230
206
  end
231
207
 
232
208
  private
233
209
  def sanitize args
234
- dup_args = args
235
210
  SANITATION.each do |key|
236
- dup_args.delete(key) if args[key]
211
+ args.delete(key) if args[key]
237
212
  end
238
- dup_args
239
- end
240
-
241
- def default_time
242
- 60 * 60 * 2
213
+ args
243
214
  end
244
215
 
245
216
  end
246
-
247
217
  end
248
-
249
218
  end
250
-
@@ -1,3 +1,3 @@
1
1
  module SimpleSession
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -1,13 +1,14 @@
1
1
  require "simple_session/version"
2
2
  require "simple_session/base.rb"
3
3
 
4
- # Options
5
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
6
- # key = options.fetch :key, 'rack.session'
7
- # secret = options[:secret]
8
- # options_key = options.fetch :options_key, 'rack.session.options'
9
-
10
4
  module SimpleSession
5
+ DEFAULT_OPTS = {
6
+ max_age: 172800 ,
7
+ domain: nil,
8
+ path: '/',
9
+ http_only: false,
10
+ secure: false,
11
+ }
11
12
 
12
13
  class Session < Base
13
14
 
@@ -24,7 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "bundler", "~> 1.10"
25
25
  spec.add_development_dependency "rake", "~> 10.0"
26
26
  spec.add_development_dependency "minitest"
27
- spec.add_development_dependency "byebug"
28
27
  spec.add_development_dependency "rack-test"
29
28
  spec.add_development_dependency "sinatra"
30
29
  spec.add_development_dependency "timecop"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_session
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - hayduke19us
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-11-10 00:00:00.000000000 Z
11
+ date: 2015-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: byebug
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rack-test
71
57
  requirement: !ruby/object:Gem::Requirement