file-rack 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|