Subject: | Error calling $c->change_session_id (patch included) |
The DBIC session store plugin errors out with the following message when calling $c->change_session_id:
ERROR - Caught exception in engine "DBIx::Class::Row::update(): Can't update MyApp::Model::DB::Session=HASH(0x7f8f3d4f5c20): row not found at ~/perl5/perlbrew/perls/perl-5.20.2/lib/site_perl/5.20.2/Catalyst/Plugin/Session/Store/DBIC/Delegate.pm line 124"
I have attached a patch to fix the problem, which includes a new set of tests that can be used to recreate the issue.
The problem occurs when the old session is deleted, but the Delegate object keeps an old copy of the DBIx::Class row object in an accessor and continues to use that old object. This means the cached object keeps it's old session ID, and when it tries to flush to the database, since the ->in_storage flag for the row is still set to true, it attempts an update of the row in the database which fails.
The fix is simple. When asking for the row object, check to make sure that the row object that is cached matches the session ID that is being requested. This just requires a small change to the 'session' method in Catalyst/Plugin/Session/Store/DBIC/Delegate.pm.
I would have provided a pull request through github, but it looks like the distribution there is out of date. I hope a patch is acceptable.
Cheers,
Cees Hek
Subject: | Catalyst-Plugin-Session-Store-DBIC-0.14.patch |
Only in Catalyst-Plugin-Session-Store-DBIC-0.14: MANIFEST
Only in Catalyst-Plugin-Session-Store-DBIC-0.14: META.yml
Only in Catalyst-Plugin-Session-Store-DBIC-0.14: MYMETA.json
diff -ru Catalyst-Plugin-Session-Store-DBIC-0.14/lib/Catalyst/Plugin/Session/Store/DBIC/Delegate.pm Catalyst-Plugin-Session-Store-DBIC-0.14.patched/lib/Catalyst/Plugin/Session/Store/DBIC/Delegate.pm
--- Catalyst-Plugin-Session-Store-DBIC-0.14/lib/Catalyst/Plugin/Session/Store/DBIC/Delegate.pm 2013-07-27 04:33:16.000000000 -0400
+++ Catalyst-Plugin-Session-Store-DBIC-0.14.patched/lib/Catalyst/Plugin/Session/Store/DBIC/Delegate.pm 2016-03-03 13:52:59.000000000 -0500
@@ -31,7 +31,7 @@
my $row = $self->_session_row;
- unless ($row) {
+ if (not $row or $row->id ne $key) {
$row = $self->_load_row($key);
$self->_session_row($row);
}
diff -ru Catalyst-Plugin-Session-Store-DBIC-0.14/t/05dbic-schema.t Catalyst-Plugin-Session-Store-DBIC-0.14.patched/t/05dbic-schema.t
--- Catalyst-Plugin-Session-Store-DBIC-0.14/t/05dbic-schema.t 2013-07-27 04:33:16.000000000 -0400
+++ Catalyst-Plugin-Session-Store-DBIC-0.14.patched/t/05dbic-schema.t 2016-03-03 13:55:22.000000000 -0500
@@ -21,7 +21,7 @@
eval { require Catalyst::Model::DBIC::Schema }
or plan skip_all => "Catalyst::Model::DBIC::Schema is required for this test";
- plan tests => 14;
+ plan tests => 21;
$TestApp::DB_FILE = "$FindBin::Bin/session.db";
@@ -64,6 +64,16 @@
$mech->get_ok("http://localhost/session/output?key=$key", 'request to get session value');
$mech->content_is($value, 'got session value back');
+# Check change_session_id
+$mech->get_ok("http://localhost/session/sessionid", 'request current session ID');
+my $sid = $mech->content;
+$mech->get_ok("http://localhost/session/change", 'request to change session ID');
+$mech->content_is('ok', 'successful');
+$mech->get_ok("http://localhost/session/sessionid", 'request current session ID');
+ok($mech->content ne $sid, 'got a new session ID');
+$mech->get_ok("http://localhost/session/output?key=$key", 'request to get session value');
+$mech->content_is($value, 'got session value back');
+
# Exceed our session storage capactity
$value = "blah" x 200;
warnings_exist {
diff -ru Catalyst-Plugin-Session-Store-DBIC-0.14/t/lib/TestApp/Controller/Session.pm Catalyst-Plugin-Session-Store-DBIC-0.14.patched/t/lib/TestApp/Controller/Session.pm
--- Catalyst-Plugin-Session-Store-DBIC-0.14/t/lib/TestApp/Controller/Session.pm 2013-07-27 04:33:16.000000000 -0400
+++ Catalyst-Plugin-Session-Store-DBIC-0.14.patched/t/lib/TestApp/Controller/Session.pm 2016-03-03 13:54:33.000000000 -0500
@@ -22,6 +22,22 @@
$c->res->body($c->session->{$key});
}
+sub change : Local {
+ my ($self, $c) = @_;
+
+ my $oldsid = $c->sessionid;
+ $c->change_session_id;
+ my $newsid = $c->sessionid;
+
+ $c->res->body($oldsid ne $newsid ? 'ok' : 'not ok');
+}
+
+sub sessionid : Local {
+ my ($self, $c) = @_;
+
+ $c->res->body($c->sessionid);
+}
+
sub delete : Local {
my ($self, $c) = @_;