activerecord-session_store 2.2.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: a59c796a95093cdbf0b0239be6b391e6319aa15892c5eaa88a4f0cd21f71f730
4
- data.tar.gz: 512857df11dfe09153779282bc6924ba09e5052cc027bfcdc49911cf448eaa57
3
+ metadata.gz: 4d410be8bc8f2608aab6f3362f842ffe56498e018ca813afb080096af80ed3c1
4
+ data.tar.gz: 4e520360edec22efb0647ce4aea99f4dd3996483e0ea8d8d95b59c3b8c49e8c6
5
5
  SHA512:
6
- metadata.gz: c14348425cf37d14be0562e9c0c60b77d4279a77daa5edd1482cff8cf4757757c923dd8740a8779abc0401bfd3b761a46da541a50771c558cfd8836e4b25daf5
7
- data.tar.gz: f9357eb83b033c69010cc36fae85e5cd72db780562749aa9512aa78d7440f32666bf6c260e12177bd07c813eb8a8049478e391d0476c29c0d7956f21f99989e2
6
+ metadata.gz: c0a67cf16741647e87c32630beef7b5d51c978037eecb54157eb721d154a5081dbe2b340591b1c1f6b9597ec63aee1dbe6d865cfa82a3a375b2fe37cd249e8cb
7
+ data.tar.gz: 17b50e63572cebaf5bec61db9d1400908f947ef112cd386505022c1386914ca6520176979b64d16532747854ff273baf9dbb060c8e7c571769f54743ba8678ae
data/CHANGELOG.md CHANGED
@@ -1,3 +1 @@
1
- ## 2.2.0
2
-
3
- * Drop dependency on `multi_json`.
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.2.0".freeze
3
+ VERSION = "2.3.0".freeze
4
4
  end
5
5
  end
@@ -1,6 +1,7 @@
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
6
  require 'json'
6
7
 
@@ -8,6 +9,12 @@ 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
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-session_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activerecord
@@ -15,42 +15,42 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '7.0'
18
+ version: '7.1'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '7.0'
25
+ version: '7.1'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: actionpack
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '7.0'
32
+ version: '7.1'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '7.0'
39
+ version: '7.1'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: railties
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '7.0'
46
+ version: '7.1'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: '7.0'
53
+ version: '7.1'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: rack
56
56
  requirement: !ruby/object:Gem::Requirement
@@ -120,14 +120,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
120
120
  requirements:
121
121
  - - ">="
122
122
  - !ruby/object:Gem::Version
123
- version: 2.5.0
123
+ version: 2.7.0
124
124
  required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  requirements:
126
126
  - - ">="
127
127
  - !ruby/object:Gem::Version
128
128
  version: '0'
129
129
  requirements: []
130
- rubygems_version: 3.6.2
130
+ rubygems_version: 3.6.7
131
131
  specification_version: 4
132
132
  summary: An Action Dispatch session store backed by an Active Record class.
133
133
  test_files: []