file-rack 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rack/session/file.rb +188 -0
  3. metadata +60 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9248733450c739c3a357513ae258df3041656687
4
+ data.tar.gz: e4a605b6a64f1e141015ab74a9e86e520682a6f5
5
+ SHA512:
6
+ metadata.gz: a7d3d5db06550f5b1236c4b1baf524e92667602582fe9aaf69bf4c80d9eb9ff5529315fb0e3a08c0c110cd3b39d561689c94fe6b819d9397aeae78b7edaa115b
7
+ data.tar.gz: '00296450b26275e88bcd05621721c1f8fd90d31956d1b24d56798d1fca3635dab6724e82cdbf74b38e2384e281f561b0d48f08d19d23a367952207ea0c2c904d'
@@ -0,0 +1,188 @@
1
+ require 'pstore'
2
+ require 'tmpdir'
3
+ require 'openssl'
4
+ require 'rack/request'
5
+ require 'rack/response'
6
+ require 'rack/session/abstract/id'
7
+
8
+ module Rack
9
+
10
+ module Session
11
+
12
+ # Rack::Session::File provides simple filed based session management.
13
+ # By default, the session is stored in /tmp while cookie holds only
14
+ # session id.
15
+ #
16
+ # When the :secret key is set (recommended), cookie data is checked
17
+ # for data integrity. The :old_secret key is also accepted allowing
18
+ # smooth secret rotation.
19
+ #
20
+ # Garbage collection is controlled via :gc_probability and :gc_maxlife.
21
+ # Every call to write_session, garbage collector is called with probability
22
+ # of :gc_probability. It scans :dir for sessions files and deletes ones
23
+ # with mtime older than :gc_maxlife.
24
+ #
25
+ # Supported options for constructor are:
26
+ #
27
+ # :dir directory into which save sessions
28
+ # :prefix session file prefix
29
+ # :key under what cookie save the session_id
30
+ # :domain domain should the session_id cookie is valid for
31
+ # :path path the session_id cookie is valid for
32
+ # :expire_after session_id cookie expires after this seconds
33
+ # :secret secret to use for integrity check
34
+ # :old_secret secret previously used, allowing smooth secret rotation
35
+ #
36
+ # :gc_probability probability of gc to run, in interval [0; 1]
37
+ # :gc_maxlife how old (in seconds) session files should be cleaned up
38
+ #
39
+ # Default values:
40
+ #
41
+ # :dir File.join(Dir.tmpdir(), 'file-rack')
42
+ # :prefix 'file-rack-session-'
43
+ # :key rack.session
44
+ # :domain nil
45
+ # :path nil
46
+ # :expire_after nil
47
+ # :secret nil
48
+ # :old_secret nil
49
+ # :gc_probability 0.01
50
+ # :gc_maxlife 1200
51
+ #
52
+ # Example:
53
+ #
54
+ # use Rack::Session::File, dir: '/tmp',
55
+ # prefix: 'session-',
56
+ #
57
+ # All parameters are optional.
58
+ class File < Abstract::Persisted
59
+
60
+ SESSION_ID = 'session_id'.freeze
61
+
62
+ def initialize(app, options = {})
63
+ @secrets = options.values_at(:secret, :old_secret).compact
64
+ @hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
65
+
66
+ @dir = options[:dir] || ::File.join(Dir.tmpdir(), 'file-rack')
67
+ @prefix = options[:prefix] || 'file-rack-session-'
68
+ FileUtils.mkdir_p @dir
69
+
70
+ @gc_probability = options[:gc_probability] || 0.01
71
+ @gc_maxlife = options[:gc_maxlife] || 1200
72
+
73
+ warn <<~MSG unless secure?(options)
74
+ SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
75
+ This poses a security threat. It is strongly recommended that you
76
+ provide a secret to prevent exploits that may be possible from crafted
77
+ cookies. This will not be supported in future versions of Rack, and
78
+ future versions will even invalidate your existing user cookies.
79
+
80
+ Called from: #{caller[0]}.
81
+ MSG
82
+
83
+ super(app, options.merge!(cookie_only: false))
84
+ end
85
+
86
+ private
87
+
88
+ def find_session(req, sid)
89
+ data = load_data(req)
90
+ data = persistent_session_id(data)
91
+ [data[SESSION_ID], data]
92
+ end
93
+
94
+ def write_session(req, session_id, session, options)
95
+ if options[:renew]
96
+ session[SESSION_ID] = generate_sid
97
+ end
98
+
99
+ store = PStore.new(path_for_sid(session_id))
100
+ store.transaction { store[:session] = session }
101
+
102
+ try_gc_run!
103
+
104
+ if @secrets.first
105
+ "#{session_id}--#{generate_hmac(session_id, @secrets.first)}"
106
+ else
107
+ session_id
108
+ end
109
+ end
110
+
111
+ def delete_session(req, session_id, options)
112
+ begin
113
+ File.delete(path_for_sid(session_id))
114
+ rescue => e
115
+ warn "Cannot delete session #{session_id}: #{e}"
116
+ end
117
+
118
+ unless options[:drop]
119
+ generate_sid
120
+ else
121
+ nil
122
+ end
123
+ end
124
+
125
+ def load_data(req)
126
+ sid = req.cookies[@key]
127
+ if @secrets.size > 0 and sid
128
+ sid, digest = sid.split('--', 2)
129
+ sid = nil unless digest_match?(sid, digest)
130
+ end
131
+ if sid
132
+ store = PStore.new(path_for_sid(sid), read_only: true)
133
+ store.transaction { store[:session] }
134
+ else
135
+ {}
136
+ end
137
+ end
138
+ def persistent_session_id(data, sid = nil)
139
+ data ||= {}
140
+ data[SESSION_ID] ||= sid
141
+ unless data[SESSION_ID]
142
+ begin
143
+ data[SESSION_ID] = generate_sid
144
+ end while ::File.exist? path_for_sid(data[SESSION_ID])
145
+ end
146
+ data
147
+ end
148
+
149
+ def path_for_sid(sid)
150
+ ::File.join @dir, "#{@prefix}#{sid}"
151
+ end
152
+
153
+ def digest_match?(data, digest)
154
+ return unless data && digest
155
+ @secrets.any? do |secret|
156
+ Rack::Utils.secure_compare(digest, generate_hmac(data, secret))
157
+ end
158
+ end
159
+
160
+ def generate_hmac(data, secret)
161
+ OpenSSL::HMAC.hexdigest(@hmac.new, secret, data)
162
+ end
163
+
164
+ def secure?(options)
165
+ @secrets.size >= 1
166
+ end
167
+
168
+ def try_gc_run!
169
+ return unless Random.rand < @gc_probability
170
+
171
+ threshold = Time.now - @gc_maxlife
172
+ Dir.chdir(@dir) do
173
+ Dir.entries(@dir).each do |entry|
174
+ next unless entry[/#{@prefix}/]
175
+ begin
176
+ ::File.delete(entry) if ::File.mtime(entry) < threshold
177
+ rescue => e
178
+ warn "Cannot delete session file #{entry}: #{e}"
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ end
185
+
186
+ end
187
+
188
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: file-rack
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Gray Wolf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ description: 'File-based rack session mechanism keeping only session_id in the cookie.
28
+
29
+ '
30
+ email: file-rack@wolfsden.cz
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - lib/rack/session/file.rb
36
+ homepage: https://rubygems.org/gems/file-rack
37
+ licenses:
38
+ - MIT
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 2.6.13
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: File-based rack session mechanism
60
+ test_files: []