file-rack 1.0.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 +7 -0
- data/lib/rack/session/file.rb +188 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -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: []
|