activerecord-session_store 2.1.0 → 2.3.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
  SHA256:
3
- metadata.gz: 6bb05b1423773abedfd835936febc52ab7db2ac6e501d12ef04caeafebc8f803
4
- data.tar.gz: 021ef3c3a9dbb1d778ebfbd6afc71f2b1826f5d3589571afadf6936dd5e8ed5b
3
+ metadata.gz: 4d410be8bc8f2608aab6f3362f842ffe56498e018ca813afb080096af80ed3c1
4
+ data.tar.gz: 4e520360edec22efb0647ce4aea99f4dd3996483e0ea8d8d95b59c3b8c49e8c6
5
5
  SHA512:
6
- metadata.gz: ed3984c27305ead605fd3e4a9fd3855fb3005efca57035c80f208df06ce2180da4bc241a76d4821e8a80702e085795f0821988648b1d0065aa449d59a5a98d1e
7
- data.tar.gz: d55b4b6ff073360a3770fb4097e6a4c009a0ed186364cff2de69ba61af4dc9f7c57009fa7ca149af7cda5802a5e924ca962d6af185d5f64ef391ed68414fe576
6
+ metadata.gz: c0a67cf16741647e87c32630beef7b5d51c978037eecb54157eb721d154a5081dbe2b340591b1c1f6b9597ec63aee1dbe6d865cfa82a3a375b2fe37cd249e8cb
7
+ data.tar.gz: 17b50e63572cebaf5bec61db9d1400908f947ef112cd386505022c1386914ca6520176979b64d16532747854ff273baf9dbb060c8e7c571769f54743ba8678ae
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ See https://github.com/rails/activerecord-session_store/releases for more information on the releases.
data/README.md CHANGED
@@ -38,6 +38,13 @@ been updated in the last 30 days. The 30 days cutoff can be changed using the
38
38
  Configuration
39
39
  --------------
40
40
 
41
+ Disable fallback to use insecure session by providing the option
42
+ `secure_session_only` when setting up the session store.
43
+
44
+ ```ruby
45
+ Rails.application.config.session_store :active_record_store, key: '_my_app_session', secure_session_only: true
46
+ ```
47
+
41
48
  The default assumes a `sessions` table with columns:
42
49
 
43
50
  * `id` (numeric primary key),
@@ -136,6 +143,10 @@ $ rake db:sessions:upgrade
136
143
  This rake task is idempotent and can be run multiple times, and session data of
137
144
  users will remain intact.
138
145
 
146
+ If you are using a custom class for storing your sessions (as described earlier
147
+ in `Configuration`), you need to copy and adapt this task or add a migration
148
+ containing equivalent code.
149
+
139
150
  Please see [#151] for more details.
140
151
 
141
152
  [#151]: https://github.com/rails/activerecord-session_store/pull/151
@@ -1,5 +1,5 @@
1
1
  require "active_support/core_ext/module/attribute_accessors"
2
- require 'action_dispatch/middleware/session/abstract_store'
2
+ require "action_dispatch/middleware/session/abstract_store"
3
3
 
4
4
  module ActionDispatch
5
5
  module Session
@@ -57,8 +57,16 @@ module ActionDispatch
57
57
  # ActiveRecord::SessionStore::Session
58
58
  class_attribute :session_class
59
59
 
60
- SESSION_RECORD_KEY = 'rack.session.record'
60
+ DEFAULT_SAME_SITE = proc { |request| request.cookies_same_site_protection } # :nodoc:
61
61
  ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS
62
+ SESSION_RECORD_KEY = "rack.session.record"
63
+ SESSION_RECORD_CREATED_KEY = "rack.session.record.created"
64
+
65
+ def initialize(app, options = {})
66
+ @secure_session_only = options.delete(:secure_session_only) { false }
67
+ options[:same_site] = DEFAULT_SAME_SITE unless options.key?(:same_site)
68
+ super
69
+ end
62
70
 
63
71
  private
64
72
  def get_session(request, sid)
@@ -78,6 +86,7 @@ module ActionDispatch
78
86
  logger.silence do
79
87
  record, sid = get_session_model(request, sid)
80
88
  record.data = session_data
89
+ return sid unless record.changed? || record.new_record?
81
90
  return false unless record.save
82
91
 
83
92
  session_data = record.data
@@ -117,26 +126,47 @@ module ActionDispatch
117
126
 
118
127
  def get_session_model(request, id)
119
128
  logger.silence do
120
- model = get_session_with_fallback(id)
121
- unless model
122
- id = generate_sid
123
- model = session_class.new(:session_id => id.private_id, :data => {})
124
- model.save
125
- end
126
- if request.env[ENV_SESSION_OPTIONS_KEY][:id].nil?
127
- request.env[SESSION_RECORD_KEY] = model
129
+ if can_reuse_session_record?(request, id)
130
+ model = request.env[SESSION_RECORD_KEY]
128
131
  else
129
- request.env[SESSION_RECORD_KEY] ||= model
132
+ model = get_session_with_fallback(id)
133
+
134
+ new_record = false
135
+
136
+ unless model
137
+ id = generate_sid
138
+ model = session_class.new(:session_id => id.private_id, :data => {})
139
+ model.save
140
+ new_record = true
141
+ end
142
+
143
+ if request.env[ENV_SESSION_OPTIONS_KEY][:id].nil?
144
+ request.env[SESSION_RECORD_KEY] = model
145
+ request.env[SESSION_RECORD_CREATED_KEY] = new_record
146
+ else
147
+ unless request.env[SESSION_RECORD_KEY]
148
+ request.env[SESSION_RECORD_KEY] = model
149
+ request.env[SESSION_RECORD_CREATED_KEY] = new_record
150
+ end
151
+ end
130
152
  end
153
+
131
154
  [model, id]
132
155
  end
133
156
  end
134
157
 
158
+ def can_reuse_session_record?(request, id)
159
+ id &&
160
+ !request.env[SESSION_RECORD_CREATED_KEY] &&
161
+ (model = request.env[SESSION_RECORD_KEY]) &&
162
+ model.session_id == id.private_id
163
+ end
164
+
135
165
  def get_session_with_fallback(sid)
136
- if sid && !self.class.private_session_id?(sid.public_id)
166
+ if sid && sid.public_id.valid_encoding? && !self.class.private_session_id?(sid.public_id)
137
167
  if (secure_session = session_class.find_by_session_id(sid.private_id))
138
168
  secure_session
139
- elsif (insecure_session = session_class.find_by_session_id(sid.public_id))
169
+ elsif !@secure_session_only && (insecure_session = session_class.find_by_session_id(sid.public_id))
140
170
  insecure_session.session_id = sid.private_id # this causes the session to be secured
141
171
  insecure_session
142
172
  end
@@ -162,7 +192,6 @@ module ActionDispatch
162
192
  # user tried to retrieve a session by a private key?
163
193
  session_id =~ /\A\d+::/
164
194
  end
165
-
166
195
  end
167
196
  end
168
197
  end
@@ -4,6 +4,10 @@ module ActiveRecord
4
4
  module SessionStore
5
5
  class Railtie < Rails::Railtie
6
6
  rake_tasks { load File.expand_path("../../../tasks/database.rake", __FILE__) }
7
+
8
+ initializer "activerecord-session_store.deprecator" do |app|
9
+ app.deprecators[:"activerecord-session_store"] = SessionStore.deprecator
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -38,6 +38,10 @@ module ActiveRecord
38
38
  # Reset column info since it may be stale.
39
39
  reset_column_information
40
40
  if columns_hash['sessid']
41
+ SessionStore.deprecator.warn <<~MSG
42
+ Using a session ID column other than `session_id` is deprecated without replacement. You should migrate your session table to use `session_id`.
43
+ MSG
44
+
41
45
  def self.find_by_session_id(session_id)
42
46
  find_by_sessid(session_id)
43
47
  end
@@ -64,7 +68,10 @@ module ActiveRecord
64
68
  @data ||= self.class.deserialize(read_attribute(@@data_column_name)) || {}
65
69
  end
66
70
 
67
- attr_writer :data
71
+ def data=(data)
72
+ attribute_will_change!(@@data_column_name) if data != self.data
73
+ @data = data
74
+ end
68
75
 
69
76
  # Has the session been loaded yet?
70
77
  def loaded?
@@ -72,8 +72,6 @@ module ActiveRecord
72
72
  attr_accessor :session_id
73
73
  alias :new_record? :new_record
74
74
 
75
- attr_writer :data
76
-
77
75
  # Look for normal and serialized data, self.find_by_session_id's way of
78
76
  # telling us to postpone deserializing until the data is requested.
79
77
  # We need to handle a normal data attribute in case of a new record.
@@ -83,6 +81,7 @@ module ActiveRecord
83
81
  @data = attributes[:data]
84
82
  @serialized_data = attributes[:serialized_data]
85
83
  @new_record = @serialized_data.nil?
84
+ @data_changed = false
86
85
  end
87
86
 
88
87
  # Returns true if the record is persisted, i.e. it's not a new record
@@ -106,6 +105,15 @@ module ActiveRecord
106
105
  @data
107
106
  end
108
107
 
108
+ def data=(data)
109
+ @data_changed = true if data != self.data
110
+ @data = data
111
+ end
112
+
113
+ def changed?
114
+ persisted? && (@retrieved_by != session_id || @data_changed)
115
+ end
116
+
109
117
  def save
110
118
  return false unless loaded?
111
119
  serialized_data = self.class.serialize(data)
@@ -113,7 +121,7 @@ module ActiveRecord
113
121
 
114
122
  if @new_record
115
123
  @new_record = false
116
- connect.update <<-end_sql, 'Create session'
124
+ result = connect.update <<-end_sql, 'Create session'
117
125
  INSERT INTO #{table_name} (
118
126
  #{connect.quote_column_name(session_id_column)},
119
127
  #{connect.quote_column_name(data_column)} )
@@ -122,7 +130,7 @@ module ActiveRecord
122
130
  #{connect.quote(serialized_data)} )
123
131
  end_sql
124
132
  else
125
- connect.update <<-end_sql, 'Update session'
133
+ result = connect.update <<-end_sql, 'Update session'
126
134
  UPDATE #{table_name}
127
135
  SET
128
136
  #{connect.quote_column_name(data_column)}=#{connect.quote(serialized_data)},
@@ -130,6 +138,10 @@ module ActiveRecord
130
138
  WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(@retrieved_by)}
131
139
  end_sql
132
140
  end
141
+
142
+ @retrieved_by = @session_id
143
+ @data_changed = false
144
+ result
133
145
  end
134
146
 
135
147
  def destroy
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module SessionStore
3
- VERSION = "2.1.0".freeze
3
+ VERSION = "2.3.0".freeze
4
4
  end
5
5
  end
@@ -1,13 +1,20 @@
1
1
  require 'active_record'
2
2
  require 'active_record/session_store/version'
3
3
  require 'action_dispatch/session/active_record_store'
4
+ require 'active_support'
4
5
  require 'active_support/core_ext/hash/keys'
5
- require 'multi_json'
6
+ require 'json'
6
7
 
7
8
  module ActiveRecord
8
9
  module SessionStore
9
10
  autoload :Session, 'active_record/session_store/session'
10
11
 
12
+ class << self
13
+ def deprecator
14
+ @deprecator ||= ActiveSupport::Deprecation.new("3.0", "ActiveRecord::SessionStore")
15
+ end
16
+ end
17
+
11
18
  module ClassMethods # :nodoc:
12
19
  mattr_accessor :serializer
13
20
 
@@ -62,12 +69,12 @@ module ActiveRecord
62
69
  # Uses built-in JSON library to encode/decode session
63
70
  class JsonSerializer
64
71
  def self.load(value)
65
- hash = MultiJson.load(value)
72
+ hash = JSON.parse(value)
66
73
  hash.is_a?(Hash) ? hash.with_indifferent_access[:value] : hash
67
74
  end
68
75
 
69
76
  def self.dump(value)
70
- MultiJson.dump(value: value)
77
+ JSON.dump(value: value)
71
78
  end
72
79
  end
73
80
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-session_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-08-31 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activerecord
@@ -16,42 +15,42 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '6.1'
18
+ version: '7.1'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '6.1'
25
+ version: '7.1'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: actionpack
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: '6.1'
32
+ version: '7.1'
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: '6.1'
39
+ version: '7.1'
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: railties
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ">="
46
45
  - !ruby/object:Gem::Version
47
- version: '6.1'
46
+ version: '7.1'
48
47
  type: :runtime
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - ">="
53
52
  - !ruby/object:Gem::Version
54
- version: '6.1'
53
+ version: '7.1'
55
54
  - !ruby/object:Gem::Dependency
56
55
  name: rack
57
56
  requirement: !ruby/object:Gem::Requirement
@@ -72,26 +71,6 @@ dependencies:
72
71
  - - "<"
73
72
  - !ruby/object:Gem::Version
74
73
  version: '4'
75
- - !ruby/object:Gem::Dependency
76
- name: multi_json
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '1.11'
82
- - - ">="
83
- - !ruby/object:Gem::Version
84
- version: 1.11.2
85
- type: :runtime
86
- prerelease: false
87
- version_requirements: !ruby/object:Gem::Requirement
88
- requirements:
89
- - - "~>"
90
- - !ruby/object:Gem::Version
91
- version: '1.11'
92
- - - ">="
93
- - !ruby/object:Gem::Version
94
- version: 1.11.2
95
74
  - !ruby/object:Gem::Dependency
96
75
  name: cgi
97
76
  requirement: !ruby/object:Gem::Requirement
@@ -106,27 +85,13 @@ dependencies:
106
85
  - - ">="
107
86
  - !ruby/object:Gem::Version
108
87
  version: 0.3.6
109
- - !ruby/object:Gem::Dependency
110
- name: sqlite3
111
- requirement: !ruby/object:Gem::Requirement
112
- requirements:
113
- - - ">="
114
- - !ruby/object:Gem::Version
115
- version: '0'
116
- type: :development
117
- prerelease: false
118
- version_requirements: !ruby/object:Gem::Requirement
119
- requirements:
120
- - - ">="
121
- - !ruby/object:Gem::Version
122
- version: '0'
123
- description:
124
88
  email: david@loudthinking.com
125
89
  executables: []
126
90
  extensions: []
127
91
  extra_rdoc_files:
128
92
  - README.md
129
93
  files:
94
+ - CHANGELOG.md
130
95
  - MIT-LICENSE
131
96
  - README.md
132
97
  - lib/action_dispatch/session/active_record_store.rb
@@ -142,8 +107,10 @@ files:
142
107
  homepage: https://github.com/rails/activerecord-session_store
143
108
  licenses:
144
109
  - MIT
145
- metadata: {}
146
- post_install_message:
110
+ metadata:
111
+ homepage_uri: https://github.com/rails/activerecord-session_store
112
+ source_code_uri: https://github.com/rails/activerecord-session_store
113
+ changelog_uri: https://github.com/rails/activerecord-session_store/blob/master/CHANGELOG.md
147
114
  rdoc_options:
148
115
  - "--main"
149
116
  - README.md
@@ -153,15 +120,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
153
120
  requirements:
154
121
  - - ">="
155
122
  - !ruby/object:Gem::Version
156
- version: 2.5.0
123
+ version: 2.7.0
157
124
  required_rubygems_version: !ruby/object:Gem::Requirement
158
125
  requirements:
159
126
  - - ">="
160
127
  - !ruby/object:Gem::Version
161
128
  version: '0'
162
129
  requirements: []
163
- rubygems_version: 3.3.7
164
- signing_key:
130
+ rubygems_version: 3.6.7
165
131
  specification_version: 4
166
132
  summary: An Action Dispatch session store backed by an Active Record class.
167
133
  test_files: []