line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
use Moose; |
3
|
3
|
|
|
3
|
|
298865
|
with 'MooseX::Emulate::Class::Accessor::Fast'; |
|
3
|
|
|
|
|
1077915
|
|
|
3
|
|
|
|
|
17
|
|
4
|
|
|
|
|
|
|
use MRO::Compat; |
5
|
3
|
|
|
3
|
|
18224
|
use Catalyst::Exception (); |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
61
|
|
6
|
3
|
|
|
3
|
|
1579
|
use Digest (); |
|
3
|
|
|
|
|
226218
|
|
|
3
|
|
|
|
|
95
|
|
7
|
3
|
|
|
3
|
|
1614
|
use overload (); |
|
3
|
|
|
|
|
1544
|
|
|
3
|
|
|
|
|
57
|
|
8
|
3
|
|
|
3
|
|
18
|
use Object::Signature (); |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
38
|
|
9
|
3
|
|
|
3
|
|
1040
|
use HTML::Entities (); |
|
3
|
|
|
|
|
10645
|
|
|
3
|
|
|
|
|
57
|
|
10
|
3
|
|
|
3
|
|
1312
|
use Carp; |
|
3
|
|
|
|
|
14883
|
|
|
3
|
|
|
|
|
128
|
|
11
|
3
|
|
|
3
|
|
27
|
use List::Util qw/ max /; |
|
3
|
|
|
|
|
64
|
|
|
3
|
|
|
|
|
217
|
|
12
|
3
|
|
|
3
|
|
17
|
|
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
172
|
|
13
|
|
|
|
|
|
|
use namespace::clean -except => 'meta'; |
14
|
3
|
|
|
3
|
|
20
|
|
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
27
|
|
15
|
|
|
|
|
|
|
our $VERSION = '0.42'; |
16
|
|
|
|
|
|
|
$VERSION =~ tr/_//d; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
my @session_data_accessors; # used in delete_session |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors( |
21
|
|
|
|
|
|
|
"_session_delete_reason", |
22
|
|
|
|
|
|
|
@session_data_accessors = qw/ |
23
|
|
|
|
|
|
|
_sessionid |
24
|
|
|
|
|
|
|
_session |
25
|
|
|
|
|
|
|
_session_expires |
26
|
|
|
|
|
|
|
_extended_session_expires |
27
|
|
|
|
|
|
|
_session_data_sig |
28
|
|
|
|
|
|
|
_flash |
29
|
|
|
|
|
|
|
_flash_keep_keys |
30
|
|
|
|
|
|
|
_flash_key_hashes |
31
|
|
|
|
|
|
|
_tried_loading_session_id |
32
|
|
|
|
|
|
|
_tried_loading_session_data |
33
|
|
|
|
|
|
|
_tried_loading_session_expires |
34
|
|
|
|
|
|
|
_tried_loading_flash_data |
35
|
|
|
|
|
|
|
_needs_early_session_finalization |
36
|
|
|
|
|
|
|
/ |
37
|
|
|
|
|
|
|
); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my $c = shift; |
40
|
|
|
|
|
|
|
# FIXME - Start warning once all the state/store modules have also been updated. |
41
|
15
|
|
|
15
|
|
20
|
#$c->log->warn("Deprecated 'session' config key used, please use the key 'Plugin::Session' instead") |
42
|
|
|
|
|
|
|
# if exists $c->config->{session} |
43
|
|
|
|
|
|
|
#$c->config->{'Plugin::Session'} ||= delete($c->config->{session}) || {}; |
44
|
|
|
|
|
|
|
$c->config->{'Plugin::Session'} ||= $c->config->{session} || {}; |
45
|
|
|
|
|
|
|
} |
46
|
15
|
|
100
|
|
|
29
|
|
|
|
|
33
|
|
|
|
|
47
|
|
|
|
|
|
|
my $c = shift; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
$c->maybe::next::method(@_); |
50
|
5
|
|
|
5
|
1
|
14342
|
|
51
|
|
|
|
|
|
|
$c->check_session_plugin_requirements; |
52
|
5
|
|
|
|
|
19
|
$c->setup_session; |
53
|
|
|
|
|
|
|
|
54
|
5
|
|
|
|
|
59
|
return $c; |
55
|
2
|
|
|
|
|
36
|
} |
56
|
|
|
|
|
|
|
|
57
|
2
|
|
|
|
|
16
|
my $c = shift; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
unless ( $c->isa("Catalyst::Plugin::Session::State") |
60
|
|
|
|
|
|
|
&& $c->isa("Catalyst::Plugin::Session::Store") ) |
61
|
5
|
|
|
5
|
1
|
7
|
{ |
62
|
|
|
|
|
|
|
my $err = |
63
|
5
|
100
|
100
|
|
|
11
|
( "The Session plugin requires both Session::State " |
64
|
|
|
|
|
|
|
. "and Session::Store plugins to be used as well." ); |
65
|
|
|
|
|
|
|
|
66
|
3
|
|
|
|
|
53
|
$c->log->fatal($err); |
67
|
|
|
|
|
|
|
Catalyst::Exception->throw($err); |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
} |
70
|
3
|
|
|
|
|
8
|
|
71
|
3
|
|
|
|
|
24
|
my $c = shift; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
my $cfg = $c->_session_plugin_config; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
%$cfg = ( |
76
|
2
|
|
|
2
|
1
|
5
|
expires => 7200, |
77
|
|
|
|
|
|
|
verify_address => 0, |
78
|
2
|
|
|
|
|
5
|
verify_user_agent => 0, |
79
|
|
|
|
|
|
|
expiry_threshold => 0, |
80
|
2
|
|
|
|
|
25
|
%$cfg, |
81
|
|
|
|
|
|
|
); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
$c->maybe::next::method(); |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
my $c = shift; |
87
|
|
|
|
|
|
|
|
88
|
2
|
|
|
|
|
5
|
$c->maybe::next::method(@_); |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
if ( $c->_session_plugin_config->{flash_to_stash} |
91
|
|
|
|
|
|
|
and $c->sessionid |
92
|
1
|
|
|
1
|
1
|
15
|
and my $flash_data = $c->flash ) |
93
|
|
|
|
|
|
|
{ |
94
|
1
|
|
|
|
|
5
|
@{ $c->stash }{ keys %$flash_data } = values %$flash_data; |
95
|
|
|
|
|
|
|
} |
96
|
1
|
50
|
33
|
|
|
13
|
} |
|
|
|
33
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
my $c = shift; |
99
|
|
|
|
|
|
|
|
100
|
1
|
|
|
|
|
98
|
# fix cookie before we send headers |
|
1
|
|
|
|
|
20
|
|
101
|
|
|
|
|
|
|
$c->_save_session_expires; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# Force extension of session_expires before finalizing headers, so a pos |
104
|
|
|
|
|
|
|
# up to date. First call to session_expires will extend the expiry, subs |
105
|
0
|
|
|
0
|
1
|
0
|
# just return the previously extended value. |
106
|
|
|
|
|
|
|
$c->session_expires; |
107
|
|
|
|
|
|
|
$c->finalize_session if $c->_needs_early_session_finalization; |
108
|
0
|
|
|
|
|
0
|
|
109
|
|
|
|
|
|
|
return $c->maybe::next::method(@_); |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
my $c = shift; |
113
|
0
|
|
|
|
|
0
|
|
114
|
0
|
0
|
|
|
|
0
|
# We have to finalize our session *before* $c->engine->finalize_xxx is called, |
115
|
|
|
|
|
|
|
# because we do not want to send the HTTP response before the session is stored/committed to |
116
|
0
|
|
|
|
|
0
|
# the session database (or whatever Session::Store you use). |
117
|
|
|
|
|
|
|
$c->finalize_session unless $c->_needs_early_session_finalization; |
118
|
|
|
|
|
|
|
$c->_clear_session_instance_data; |
119
|
|
|
|
|
|
|
|
120
|
6
|
|
|
6
|
1
|
2665
|
return $c->maybe::next::method(@_); |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
my $c = shift; |
124
|
|
|
|
|
|
|
|
125
|
6
|
50
|
|
|
|
17
|
$c->maybe::next::method(@_); |
126
|
6
|
|
|
|
|
35
|
|
127
|
|
|
|
|
|
|
$c->_save_session_id; |
128
|
6
|
|
|
|
|
57
|
$c->_save_session; |
129
|
|
|
|
|
|
|
$c->_save_flash; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
} |
132
|
6
|
|
|
6
|
1
|
608
|
|
133
|
|
|
|
|
|
|
my $c = shift; |
134
|
6
|
|
|
|
|
25
|
|
135
|
|
|
|
|
|
|
if ( my $session_data = $c->_session ) { |
136
|
6
|
|
|
|
|
75
|
|
137
|
6
|
|
|
|
|
17
|
no warnings 'uninitialized'; |
138
|
6
|
|
|
|
|
18
|
if ( Object::Signature::signature($session_data) ne |
139
|
|
|
|
|
|
|
$c->_session_data_sig ) |
140
|
|
|
|
|
|
|
{ |
141
|
|
|
|
|
|
|
return $session_data; |
142
|
|
|
|
|
|
|
} else { |
143
|
12
|
|
|
12
|
|
15
|
return; |
144
|
|
|
|
|
|
|
} |
145
|
12
|
100
|
|
|
|
24
|
|
146
|
|
|
|
|
|
|
} else { |
147
|
3
|
|
|
3
|
|
2592
|
|
|
3
|
|
|
|
|
14
|
|
|
3
|
|
|
|
|
1621
|
|
148
|
11
|
100
|
|
|
|
1036
|
return; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
} |
151
|
7
|
|
|
|
|
977
|
} |
152
|
|
|
|
|
|
|
|
153
|
4
|
|
|
|
|
570
|
my $c = shift; |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# we already called set when allocating |
156
|
|
|
|
|
|
|
# no need to tell the state plugins anything new |
157
|
|
|
|
|
|
|
} |
158
|
1
|
|
|
|
|
98
|
|
159
|
|
|
|
|
|
|
my $c = shift; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
if ( defined($c->_session_expires) ) { |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
if (my $sid = $c->sessionid) { |
164
|
6
|
|
|
6
|
|
7
|
|
165
|
|
|
|
|
|
|
my $current = $c->_get_stored_session_expires; |
166
|
|
|
|
|
|
|
my $extended = $c->session_expires; |
167
|
|
|
|
|
|
|
if ($extended > $current) { |
168
|
|
|
|
|
|
|
$c->store_session_data( "expires:$sid" => $extended ); |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
0
|
|
|
0
|
|
0
|
} |
172
|
|
|
|
|
|
|
} |
173
|
0
|
0
|
|
|
|
0
|
} |
174
|
|
|
|
|
|
|
|
175
|
0
|
0
|
|
|
|
0
|
my $c = shift; |
176
|
|
|
|
|
|
|
|
177
|
0
|
|
|
|
|
0
|
if ( my $session_data = $c->_session_updated ) { |
178
|
0
|
|
|
|
|
0
|
|
179
|
0
|
0
|
|
|
|
0
|
$session_data->{__updated} = time(); |
180
|
0
|
|
|
|
|
0
|
my $sid = $c->sessionid; |
181
|
|
|
|
|
|
|
$c->store_session_data( "session:$sid" => $session_data ); |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
my $c = shift; |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
if ( my $flash_data = $c->_flash ) { |
188
|
12
|
|
|
12
|
|
17
|
|
189
|
|
|
|
|
|
|
my $hashes = $c->_flash_key_hashes || {}; |
190
|
12
|
100
|
|
|
|
21
|
my $keep = $c->_flash_keep_keys || {}; |
191
|
|
|
|
|
|
|
foreach my $key ( keys %$hashes ) { |
192
|
7
|
|
|
|
|
12
|
if ( !exists $keep->{$key} and Object::Signature::signature( \$flash_data->{$key} ) eq $hashes->{$key} ) { |
193
|
7
|
|
|
|
|
13
|
delete $flash_data->{$key}; |
194
|
7
|
|
|
|
|
38
|
} |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
my $sid = $c->sessionid; |
198
|
|
|
|
|
|
|
|
199
|
6
|
|
|
6
|
|
8
|
my $session_data = $c->_session; |
200
|
|
|
|
|
|
|
if (%$flash_data) { |
201
|
6
|
50
|
|
|
|
14
|
$session_data->{__flash} = $flash_data; |
202
|
|
|
|
|
|
|
} |
203
|
6
|
|
100
|
|
|
557
|
else { |
204
|
6
|
|
100
|
|
|
571
|
delete $session_data->{__flash}; |
205
|
6
|
|
|
|
|
563
|
} |
206
|
2
|
50
|
33
|
|
|
10
|
$c->_session($session_data); |
207
|
2
|
|
|
|
|
56
|
$c->_save_session; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
6
|
|
|
|
|
14
|
my $c = shift; |
212
|
|
|
|
|
|
|
return $c->_session_expires if $c->_tried_loading_session_expires; |
213
|
6
|
|
|
|
|
26
|
$c->_tried_loading_session_expires(1); |
214
|
6
|
100
|
|
|
|
565
|
|
215
|
4
|
|
|
|
|
7
|
if ( my $sid = $c->sessionid ) { |
216
|
|
|
|
|
|
|
my $expires = $c->_get_stored_session_expires; |
217
|
|
|
|
|
|
|
|
218
|
2
|
|
|
|
|
5
|
if ( $expires >= time() ) { |
219
|
|
|
|
|
|
|
$c->_session_expires( $expires ); |
220
|
6
|
|
|
|
|
14
|
return $expires; |
221
|
6
|
|
|
|
|
1780
|
} else { |
222
|
|
|
|
|
|
|
$c->delete_session( "session expired" ); |
223
|
|
|
|
|
|
|
return 0; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
} |
226
|
6
|
|
|
6
|
|
9
|
|
227
|
6
|
50
|
|
|
|
13
|
return; |
228
|
6
|
|
|
|
|
559
|
} |
229
|
|
|
|
|
|
|
|
230
|
6
|
50
|
|
|
|
1512
|
my $c = shift; |
231
|
6
|
|
|
|
|
29
|
return $c->_session if $c->_tried_loading_session_data; |
232
|
|
|
|
|
|
|
$c->_tried_loading_session_data(1); |
233
|
6
|
50
|
|
|
|
47
|
|
234
|
6
|
|
|
|
|
19
|
if ( my $sid = $c->sessionid ) { |
235
|
6
|
|
|
|
|
1522
|
if ( $c->_load_session_expires ) { # > 0 |
236
|
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
0
|
my $session_data = $c->get_session_data("session:$sid") || return; |
238
|
0
|
|
|
|
|
0
|
$c->_session($session_data); |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
no warnings 'uninitialized'; # ne __address |
241
|
|
|
|
|
|
|
if ( $c->_session_plugin_config->{verify_address} |
242
|
0
|
|
|
|
|
0
|
&& exists $session_data->{__address} |
243
|
|
|
|
|
|
|
&& $session_data->{__address} ne $c->request->address ) |
244
|
|
|
|
|
|
|
{ |
245
|
|
|
|
|
|
|
$c->log->warn( |
246
|
6
|
|
|
6
|
|
642
|
"Deleting session $sid due to address mismatch (" |
247
|
6
|
50
|
|
|
|
40
|
. $session_data->{__address} . " != " |
248
|
6
|
|
|
|
|
583
|
. $c->request->address . ")" |
249
|
|
|
|
|
|
|
); |
250
|
6
|
50
|
|
|
|
1536
|
$c->delete_session("address mismatch"); |
251
|
6
|
50
|
|
|
|
26
|
return; |
252
|
|
|
|
|
|
|
} |
253
|
6
|
|
50
|
|
|
17
|
if ( $c->_session_plugin_config->{verify_user_agent} |
254
|
6
|
|
|
|
|
38
|
&& $session_data->{__user_agent} ne $c->request->user_agent ) |
255
|
|
|
|
|
|
|
{ |
256
|
3
|
|
|
3
|
|
21
|
$c->log->warn( |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
7741
|
|
257
|
6
|
0
|
33
|
|
|
1712
|
"Deleting session $sid due to user agent mismatch (" |
|
|
|
0
|
|
|
|
|
258
|
|
|
|
|
|
|
. $session_data->{__user_agent} . " != " |
259
|
|
|
|
|
|
|
. $c->request->user_agent . ")" |
260
|
|
|
|
|
|
|
); |
261
|
|
|
|
|
|
|
$c->delete_session("user agent mismatch"); |
262
|
|
|
|
|
|
|
return; |
263
|
0
|
|
|
|
|
0
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
$c->log->debug(qq/Restored session "$sid"/) if $c->debug; |
266
|
0
|
|
|
|
|
0
|
$c->_session_data_sig( Object::Signature::signature($session_data) ) if $session_data; |
267
|
0
|
|
|
|
|
0
|
$c->_expire_session_keys; |
268
|
|
|
|
|
|
|
|
269
|
6
|
50
|
33
|
|
|
77
|
return $session_data; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
return; |
274
|
0
|
|
|
|
|
0
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
my $c = shift; |
277
|
0
|
|
|
|
|
0
|
return $c->_flash if $c->_tried_loading_flash_data; |
278
|
0
|
|
|
|
|
0
|
$c->_tried_loading_flash_data(1); |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
if ( my $sid = $c->sessionid ) { |
281
|
6
|
50
|
|
|
|
51
|
|
282
|
6
|
50
|
|
|
|
30
|
my $session_data = $c->session; |
283
|
6
|
|
|
|
|
1828
|
$c->_flash($session_data->{__flash}); |
284
|
|
|
|
|
|
|
|
285
|
6
|
|
|
|
|
21
|
if ( my $flash_data = $c->_flash ) |
286
|
|
|
|
|
|
|
{ |
287
|
|
|
|
|
|
|
$c->_flash_key_hashes({ map { $_ => Object::Signature::signature( \$flash_data->{$_} ) } keys %$flash_data }); |
288
|
|
|
|
|
|
|
|
289
|
0
|
|
|
|
|
0
|
return $flash_data; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
6
|
|
|
6
|
|
595
|
return; |
294
|
6
|
50
|
|
|
|
16
|
} |
295
|
6
|
|
|
|
|
566
|
|
296
|
|
|
|
|
|
|
my ( $c, $data ) = @_; |
297
|
6
|
50
|
|
|
|
1550
|
|
298
|
|
|
|
|
|
|
my $now = time; |
299
|
6
|
|
|
|
|
29
|
|
300
|
6
|
|
|
|
|
17
|
my $expire_times = ( $data || $c->_session || {} )->{__expire_keys} || {}; |
301
|
|
|
|
|
|
|
foreach my $key ( grep { $expire_times->{$_} < $now } keys %$expire_times ) { |
302
|
6
|
100
|
|
|
|
1615
|
delete $c->_session->{$key}; |
303
|
|
|
|
|
|
|
delete $expire_times->{$key}; |
304
|
3
|
|
|
|
|
278
|
} |
|
3
|
|
|
|
|
10
|
|
305
|
|
|
|
|
|
|
} |
306
|
3
|
|
|
|
|
923
|
|
307
|
|
|
|
|
|
|
my $c = shift; |
308
|
|
|
|
|
|
|
$c->$_(undef) for @session_data_accessors; |
309
|
|
|
|
|
|
|
$c->maybe::next::method(@_); # allow other plugins to hook in on this |
310
|
3
|
|
|
|
|
313
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
my $c = shift; |
313
|
|
|
|
|
|
|
|
314
|
6
|
|
|
6
|
|
11
|
my $sessiondata = $c->session; |
315
|
|
|
|
|
|
|
my $oldsid = $c->sessionid; |
316
|
6
|
|
|
|
|
10
|
my $newsid = $c->create_session_id; |
317
|
|
|
|
|
|
|
|
318
|
6
|
|
50
|
|
|
21
|
if ($oldsid) { |
319
|
6
|
|
|
|
|
616
|
$c->log->debug(qq/change_sessid: deleting session data from "$oldsid"/) if $c->debug; |
|
0
|
|
|
|
|
0
|
|
320
|
0
|
|
|
|
|
0
|
$c->delete_session_data("${_}:${oldsid}") for qw/session expires flash/; |
321
|
0
|
|
|
|
|
0
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
$c->log->debug(qq/change_sessid: storing session data to "$newsid"/) if $c->debug; |
324
|
|
|
|
|
|
|
$c->store_session_data( "session:$newsid" => $sessiondata ); |
325
|
|
|
|
|
|
|
|
326
|
6
|
|
|
6
|
|
11
|
return $newsid; |
327
|
6
|
|
|
|
|
18
|
} |
328
|
6
|
|
|
|
|
18031
|
|
329
|
|
|
|
|
|
|
my ( $c, $msg ) = @_; |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
$c->log->debug("Deleting session" . ( defined($msg) ? "($msg)" : '(no reason given)') ) if $c->debug; |
332
|
0
|
|
|
0
|
1
|
0
|
|
333
|
|
|
|
|
|
|
# delete the session data |
334
|
0
|
|
|
|
|
0
|
if ( my $sid = $c->sessionid ) { |
335
|
0
|
|
|
|
|
0
|
$c->delete_session_data("${_}:${sid}") for qw/session expires flash/; |
336
|
0
|
|
|
|
|
0
|
$c->delete_session_id($sid); |
337
|
|
|
|
|
|
|
} |
338
|
0
|
0
|
|
|
|
0
|
|
339
|
0
|
0
|
|
|
|
0
|
# reset the values in the context object |
340
|
0
|
|
|
|
|
0
|
# see the BEGIN block |
341
|
|
|
|
|
|
|
$c->_clear_session_instance_data; |
342
|
|
|
|
|
|
|
|
343
|
0
|
0
|
|
|
|
0
|
$c->_session_delete_reason($msg); |
344
|
0
|
|
|
|
|
0
|
} |
345
|
|
|
|
|
|
|
|
346
|
0
|
|
|
|
|
0
|
my $c = shift; |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
$c->session_is_valid; # check that it was loaded |
349
|
|
|
|
|
|
|
|
350
|
0
|
|
|
0
|
1
|
0
|
$c->_session_delete_reason(@_); |
351
|
|
|
|
|
|
|
} |
352
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
353
|
|
|
|
|
|
|
my $c = shift; |
354
|
|
|
|
|
|
|
|
355
|
0
|
0
|
|
|
|
0
|
if ( defined( my $expires = $c->_extended_session_expires ) ) { |
356
|
0
|
|
|
|
|
0
|
return $expires; |
357
|
0
|
|
|
|
|
0
|
} elsif ( defined( $expires = $c->_load_session_expires ) ) { |
358
|
|
|
|
|
|
|
return $c->extend_session_expires( $expires ); |
359
|
|
|
|
|
|
|
} else { |
360
|
|
|
|
|
|
|
return 0; |
361
|
|
|
|
|
|
|
} |
362
|
0
|
|
|
|
|
0
|
} |
363
|
|
|
|
|
|
|
|
364
|
0
|
|
|
|
|
0
|
my ( $c, $expires ) = @_; |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
my $threshold = $c->_session_plugin_config->{expiry_threshold} || 0; |
367
|
|
|
|
|
|
|
|
368
|
0
|
|
|
0
|
1
|
0
|
if ( my $sid = $c->sessionid ) { |
369
|
|
|
|
|
|
|
my $expires = $c->_get_stored_session_expires; |
370
|
0
|
|
|
|
|
0
|
my $cutoff = $expires - $threshold; |
371
|
|
|
|
|
|
|
|
372
|
0
|
|
|
|
|
0
|
if (!$threshold || $cutoff <= time || $c->_session_updated) { |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
$c->_extended_session_expires( my $updated = $c->calculate_initial_session_expires() ); |
375
|
|
|
|
|
|
|
$c->extend_session_id( $sid, $updated ); |
376
|
0
|
|
|
0
|
1
|
0
|
|
377
|
|
|
|
|
|
|
return $updated; |
378
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
379
|
0
|
|
|
|
|
0
|
} else { |
380
|
|
|
|
|
|
|
|
381
|
0
|
|
|
|
|
0
|
return $expires; |
382
|
|
|
|
|
|
|
|
383
|
0
|
|
|
|
|
0
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
} else { |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
return; |
388
|
0
|
|
|
0
|
1
|
0
|
|
389
|
|
|
|
|
|
|
} |
390
|
0
|
|
0
|
|
|
0
|
|
391
|
|
|
|
|
|
|
} |
392
|
0
|
0
|
|
|
|
0
|
|
393
|
0
|
|
|
|
|
0
|
my ( $c, $expires ) = @_; |
394
|
0
|
|
|
|
|
0
|
|
395
|
|
|
|
|
|
|
$expires ||= 0; |
396
|
0
|
0
|
0
|
|
|
0
|
my $sid = $c->sessionid; |
|
|
|
0
|
|
|
|
|
397
|
|
|
|
|
|
|
my $time_exp = time() + $expires; |
398
|
0
|
|
|
|
|
0
|
$c->store_session_data( "expires:$sid" => $time_exp ); |
399
|
0
|
|
|
|
|
0
|
} |
400
|
|
|
|
|
|
|
|
401
|
0
|
|
|
|
|
0
|
my ($c) = @_; |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
if ( my $sid = $c->sessionid ) { |
404
|
|
|
|
|
|
|
return $c->get_session_data("expires:$sid") || 0; |
405
|
0
|
|
|
|
|
0
|
} else { |
406
|
|
|
|
|
|
|
return 0; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
my $c = shift; |
411
|
0
|
|
|
|
|
0
|
return ( time() + $c->_session_plugin_config->{expires} ); |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
my ($c) = @_; |
415
|
|
|
|
|
|
|
return max( $c->initial_session_expires, $c->_get_stored_session_expires ); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
0
|
|
|
0
|
1
|
0
|
my ( $c, $prev ) = @_; |
419
|
|
|
|
|
|
|
return ( time() + $prev ); |
420
|
0
|
|
0
|
|
|
0
|
} |
421
|
0
|
|
|
|
|
0
|
|
422
|
0
|
|
|
|
|
0
|
my ( $c, $sid ) = @_; |
423
|
0
|
|
|
|
|
0
|
|
424
|
|
|
|
|
|
|
my $exp = $c->calculate_initial_session_expires; |
425
|
|
|
|
|
|
|
$c->_session_expires( $exp ); |
426
|
|
|
|
|
|
|
# |
427
|
6
|
|
|
6
|
|
12
|
# since we're setting _session_expires directly, make load_session_expires |
428
|
|
|
|
|
|
|
# actually use that value. |
429
|
6
|
50
|
|
|
|
8
|
# |
430
|
6
|
|
50
|
|
|
29
|
$c->_tried_loading_session_expires(1); |
431
|
|
|
|
|
|
|
$c->_extended_session_expires( $exp ); |
432
|
0
|
|
|
|
|
0
|
$exp; |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
my $c = shift; |
436
|
|
|
|
|
|
|
|
437
|
0
|
|
|
0
|
1
|
0
|
return $c->_sessionid || $c->_load_sessionid; |
438
|
0
|
|
|
|
|
0
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
my $c = shift; |
441
|
|
|
|
|
|
|
return if $c->_tried_loading_session_id; |
442
|
0
|
|
|
0
|
1
|
0
|
$c->_tried_loading_session_id(1); |
443
|
0
|
|
|
|
|
0
|
|
444
|
|
|
|
|
|
|
if ( defined( my $sid = $c->get_session_id ) ) { |
445
|
|
|
|
|
|
|
if ( $c->validate_session_id($sid) ) { |
446
|
|
|
|
|
|
|
# temporarily set the inner key, so that validation will work |
447
|
0
|
|
|
0
|
1
|
0
|
$c->_sessionid($sid); |
448
|
0
|
|
|
|
|
0
|
return $sid; |
449
|
|
|
|
|
|
|
} else { |
450
|
|
|
|
|
|
|
$sid = HTML::Entities::encode_entities($sid); |
451
|
|
|
|
|
|
|
my $err = "Tried to set invalid session ID '$sid'"; |
452
|
0
|
|
|
0
|
1
|
0
|
$c->log->error($err); |
453
|
|
|
|
|
|
|
Catalyst::Exception->throw($err); |
454
|
0
|
|
|
|
|
0
|
} |
455
|
0
|
|
|
|
|
0
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
return; |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
0
|
|
|
|
|
0
|
my $c = shift; |
461
|
0
|
|
|
|
|
0
|
|
462
|
0
|
|
|
|
|
0
|
# force a check for expiry, but also __address, etc |
463
|
|
|
|
|
|
|
if ( $c->_load_session ) { |
464
|
|
|
|
|
|
|
return 1; |
465
|
|
|
|
|
|
|
} else { |
466
|
41
|
|
|
41
|
1
|
67
|
return; |
467
|
|
|
|
|
|
|
} |
468
|
41
|
|
33
|
|
|
79
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
my ( $c, $sid ) = @_; |
471
|
|
|
|
|
|
|
|
472
|
0
|
|
|
0
|
|
0
|
$sid and $sid =~ /^[a-f\d]+$/i; |
473
|
0
|
0
|
|
|
|
0
|
} |
474
|
0
|
|
|
|
|
0
|
|
475
|
|
|
|
|
|
|
my $c = shift; |
476
|
0
|
0
|
|
|
|
0
|
|
477
|
0
|
0
|
|
|
|
0
|
my $session = $c->_session || $c->_load_session || do { |
478
|
|
|
|
|
|
|
$c->create_session_id_if_needed; |
479
|
0
|
|
|
|
|
0
|
$c->initialize_session_data; |
480
|
0
|
|
|
|
|
0
|
}; |
481
|
|
|
|
|
|
|
|
482
|
0
|
|
|
|
|
0
|
if (@_) { |
483
|
0
|
|
|
|
|
0
|
my $new_values = @_ > 1 ? { @_ } : $_[0]; |
484
|
0
|
|
|
|
|
0
|
croak('session takes a hash or hashref') unless ref $new_values; |
485
|
0
|
|
|
|
|
0
|
|
486
|
|
|
|
|
|
|
for my $key (keys %$new_values) { |
487
|
|
|
|
|
|
|
$session->{$key} = $new_values->{$key}; |
488
|
|
|
|
|
|
|
} |
489
|
0
|
|
|
|
|
0
|
} |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
$session; |
492
|
|
|
|
|
|
|
} |
493
|
0
|
|
|
0
|
1
|
0
|
|
494
|
|
|
|
|
|
|
my ( $c, @keys ) = @_; |
495
|
|
|
|
|
|
|
my $href = $c->_flash_keep_keys || $c->_flash_keep_keys({}); |
496
|
0
|
0
|
|
|
|
0
|
(@{$href}{@keys}) = ((undef) x @keys); |
497
|
0
|
|
|
|
|
0
|
} |
498
|
|
|
|
|
|
|
|
499
|
0
|
|
|
|
|
0
|
my $c = shift; |
500
|
|
|
|
|
|
|
$c->_flash || $c->_load_flash || do { |
501
|
|
|
|
|
|
|
$c->create_session_id_if_needed; |
502
|
|
|
|
|
|
|
$c->_flash( {} ); |
503
|
|
|
|
|
|
|
}; |
504
|
0
|
|
|
0
|
1
|
0
|
} |
505
|
|
|
|
|
|
|
|
506
|
0
|
0
|
|
|
|
0
|
my $c = shift; |
507
|
|
|
|
|
|
|
if (@_) { |
508
|
|
|
|
|
|
|
my $items = @_ > 1 ? {@_} : $_[0]; |
509
|
|
|
|
|
|
|
croak('flash takes a hash or hashref') unless ref $items; |
510
|
10
|
|
|
10
|
1
|
4449
|
@{ $c->_flash }{ keys %$items } = values %$items; |
511
|
|
|
|
|
|
|
} |
512
|
10
|
|
33
|
|
|
28
|
} |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
my $c = shift; |
515
|
|
|
|
|
|
|
$c->_flash_data; |
516
|
|
|
|
|
|
|
$c->_set_flash(@_); |
517
|
10
|
50
|
|
|
|
433
|
return $c->_flash; |
518
|
0
|
0
|
|
|
|
0
|
} |
519
|
0
|
0
|
|
|
|
0
|
|
520
|
|
|
|
|
|
|
my $c = shift; |
521
|
0
|
|
|
|
|
0
|
|
522
|
0
|
|
|
|
|
0
|
#$c->delete_session_data("flash:" . $c->sessionid); # should this be in here? or delayed till finalization? |
523
|
|
|
|
|
|
|
$c->_flash_key_hashes({}); |
524
|
|
|
|
|
|
|
$c->_flash_keep_keys({}); |
525
|
|
|
|
|
|
|
$c->_flash({}); |
526
|
10
|
|
|
|
|
31
|
} |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
my ( $c, %keys ) = @_; |
529
|
|
|
|
|
|
|
|
530
|
0
|
|
|
0
|
1
|
0
|
my $now = time; |
531
|
0
|
|
0
|
|
|
0
|
@{ $c->session->{__expire_keys} }{ keys %keys } = |
532
|
0
|
|
|
|
|
0
|
map { $now + $_ } values %keys; |
|
0
|
|
|
|
|
0
|
|
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
my $c = shift; |
536
|
13
|
|
|
13
|
|
15
|
|
537
|
13
|
100
|
100
|
|
|
42
|
my $now = time; |
538
|
3
|
|
|
|
|
13
|
|
539
|
3
|
|
|
|
|
16
|
return $c->_session( |
540
|
|
|
|
|
|
|
{ |
541
|
|
|
|
|
|
|
__created => $now, |
542
|
|
|
|
|
|
|
__updated => $now, |
543
|
|
|
|
|
|
|
|
544
|
13
|
|
|
13
|
|
15
|
( |
545
|
13
|
100
|
|
|
|
29
|
$c->_session_plugin_config->{verify_address} |
546
|
1
|
50
|
|
|
|
6
|
? ( __address => $c->request->address||'' ) |
547
|
1
|
50
|
|
|
|
4
|
: () |
548
|
1
|
|
|
|
|
4
|
), |
|
1
|
|
|
|
|
3
|
|
549
|
|
|
|
|
|
|
( |
550
|
|
|
|
|
|
|
$c->_session_plugin_config->{verify_user_agent} |
551
|
|
|
|
|
|
|
? ( __user_agent => $c->request->user_agent||'' ) |
552
|
|
|
|
|
|
|
: () |
553
|
13
|
|
|
13
|
1
|
13445
|
), |
554
|
13
|
|
|
|
|
31
|
} |
555
|
13
|
|
|
|
|
1607
|
); |
556
|
13
|
|
|
|
|
124
|
} |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
my $c = shift; |
559
|
|
|
|
|
|
|
|
560
|
1
|
|
|
1
|
1
|
19
|
my $digest = $c->_find_digest(); |
561
|
|
|
|
|
|
|
$digest->add( $c->session_hash_seed() ); |
562
|
|
|
|
|
|
|
return $digest->hexdigest; |
563
|
1
|
|
|
|
|
4
|
} |
564
|
1
|
|
|
|
|
280
|
|
565
|
1
|
|
|
|
|
276
|
my $c = shift; |
566
|
|
|
|
|
|
|
$c->create_session_id unless $c->sessionid; |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
|
569
|
0
|
|
|
0
|
1
|
0
|
my $c = shift; |
570
|
|
|
|
|
|
|
|
571
|
0
|
|
|
|
|
0
|
my $sid = $c->generate_session_id; |
572
|
0
|
|
|
|
|
0
|
|
573
|
0
|
|
|
|
|
0
|
$c->log->debug(qq/Created session "$sid"/) if $c->debug; |
|
0
|
|
|
|
|
0
|
|
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
$c->_sessionid($sid); |
576
|
|
|
|
|
|
|
$c->reset_session_expires; |
577
|
0
|
|
|
0
|
1
|
0
|
$c->set_session_id($sid); |
578
|
|
|
|
|
|
|
|
579
|
0
|
|
|
|
|
0
|
return $sid; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
my $counter; |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
my $c = shift; |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
return join( "", ++$counter, time, rand, $$, {}, overload::StrVal($c), ); |
587
|
|
|
|
|
|
|
} |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
my $usable; |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
unless ($usable) { |
592
|
|
|
|
|
|
|
foreach my $alg (qw/SHA-1 SHA-256 MD5/) { |
593
|
0
|
0
|
0
|
|
|
0
|
if ( eval { Digest->new($alg) } ) { |
|
|
0
|
0
|
|
|
|
|
594
|
|
|
|
|
|
|
$usable = $alg; |
595
|
|
|
|
|
|
|
last; |
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
Catalyst::Exception->throw( |
599
|
|
|
|
|
|
|
"Could not find a suitable Digest module. Please install " |
600
|
|
|
|
|
|
|
. "Digest::SHA1, Digest::SHA, or Digest::MD5" ) |
601
|
0
|
|
|
0
|
1
|
0
|
unless $usable; |
602
|
|
|
|
|
|
|
} |
603
|
0
|
|
|
|
|
0
|
|
604
|
0
|
|
|
|
|
0
|
return Digest->new($usable); |
605
|
0
|
|
|
|
|
0
|
} |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
my $c = shift; |
608
|
|
|
|
|
|
|
|
609
|
3
|
|
|
3
|
1
|
4
|
( |
610
|
3
|
50
|
|
|
|
6
|
$c->maybe::next::method(), |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
$c->_sessionid |
613
|
|
|
|
|
|
|
? ( [ "Session ID" => $c->sessionid ], [ Session => $c->session ], ) |
614
|
0
|
|
|
0
|
1
|
|
: () |
615
|
|
|
|
|
|
|
); |
616
|
0
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
|
618
|
0
|
0
|
|
|
|
|
|
619
|
|
|
|
|
|
|
|
620
|
0
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
621
|
0
|
|
|
|
|
|
|
622
|
0
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=pod |
624
|
0
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=head1 NAME |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
Catalyst::Plugin::Session - Generic Session plugin - ties together server side storage and client side state required to maintain session data. |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=head1 SYNOPSIS |
630
|
0
|
|
|
0
|
1
|
|
|
631
|
|
|
|
|
|
|
# To get sessions to "just work", all you need to do is use these plugins: |
632
|
0
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
use Catalyst qw/ |
634
|
|
|
|
|
|
|
Session |
635
|
|
|
|
|
|
|
Session::Store::FastMmap |
636
|
|
|
|
|
|
|
Session::State::Cookie |
637
|
|
|
|
|
|
|
/; |
638
|
0
|
0
|
|
0
|
|
|
|
639
|
0
|
|
|
|
|
|
# you can replace Store::FastMmap with Store::File - both have sensible |
640
|
0
|
0
|
|
|
|
|
# default configurations (see their docs for details) |
|
0
|
|
|
|
|
|
|
641
|
0
|
|
|
|
|
|
|
642
|
0
|
|
|
|
|
|
# more complicated backends are available for other scenarios (DBI storage, |
643
|
|
|
|
|
|
|
# etc) |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
|
646
|
0
|
0
|
|
|
|
|
# after you've loaded the plugins you can save session data |
647
|
|
|
|
|
|
|
# For example, if you are writing a shopping cart, it could be implemented |
648
|
|
|
|
|
|
|
# like this: |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
sub add_item : Local { |
651
|
0
|
|
|
|
|
|
my ( $self, $c ) = @_; |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
my $item_id = $c->req->param("item"); |
654
|
|
|
|
|
|
|
|
655
|
0
|
|
|
0
|
1
|
|
# $c->session is a hash ref, a bit like $c->stash |
656
|
|
|
|
|
|
|
# the difference is that it' preserved across requests |
657
|
|
|
|
|
|
|
|
658
|
0
|
0
|
|
|
|
|
push @{ $c->session->{items} }, $item_id; |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
$c->forward("MyView"); |
661
|
|
|
|
|
|
|
} |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
sub display_items : Local { |
664
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
# values in $c->session are restored |
667
|
0
|
|
|
0
|
1
|
|
$c->stash->{items_to_display} = |
668
|
0
|
|
|
0
|
1
|
|
[ map { MyModel->retrieve($_) } @{ $c->session->{items} } ]; |
669
|
0
|
|
|
0
|
1
|
|
|
670
|
0
|
|
|
0
|
1
|
|
$c->forward("MyView"); |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=head1 DESCRIPTION |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
The Session plugin is the base of two related parts of functionality required |
676
|
|
|
|
|
|
|
for session management in web applications. |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
The first part, the State, is getting the browser to repeat back a session key, |
679
|
|
|
|
|
|
|
so that the web application can identify the client and logically string |
680
|
|
|
|
|
|
|
several requests together into a session. |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
The second part, the Store, deals with the actual storage of information about |
683
|
|
|
|
|
|
|
the client. This data is stored so that the it may be revived for every request |
684
|
|
|
|
|
|
|
made by the same client. |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
This plugin links the two pieces together. |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
=head1 RECOMENDED BACKENDS |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=over 4 |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=item Session::State::Cookie |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
The only really sane way to do state is using cookies. |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=item Session::Store::File |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
A portable backend, based on Cache::File. |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=item Session::Store::FastMmap |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
A fast and flexible backend, based on Cache::FastMmap. |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=back |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=head1 METHODS |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=over 4 |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
=item sessionid |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
An accessor for the session ID value. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=item session |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
Returns a hash reference that might contain unserialized values from previous |
717
|
|
|
|
|
|
|
requests in the same session, and whose modified value will be saved for future |
718
|
|
|
|
|
|
|
requests. |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
This method will automatically create a new session and session ID if none |
721
|
|
|
|
|
|
|
exists. |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
You can also set session keys by passing a list of key/value pairs or a |
724
|
|
|
|
|
|
|
hashref. |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
$c->session->{foo} = "bar"; # This works. |
727
|
|
|
|
|
|
|
$c->session(one => 1, two => 2); # And this. |
728
|
|
|
|
|
|
|
$c->session({ answer => 42 }); # And this. |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=item session_expires |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
This method returns the time when the current session will expire, or 0 if |
733
|
|
|
|
|
|
|
there is no current session. If there is a session and it already expired, it |
734
|
|
|
|
|
|
|
will delete the session and return 0 as well. |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=item flash |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
This is like Ruby on Rails' flash data structure. Think of it as a stash that |
739
|
|
|
|
|
|
|
lasts for longer than one request, letting you redirect instead of forward. |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
The flash data will be cleaned up only on requests on which actually use |
742
|
|
|
|
|
|
|
$c->flash (thus allowing multiple redirections), and the policy is to delete |
743
|
|
|
|
|
|
|
all the keys which haven't changed since the flash data was loaded at the end |
744
|
|
|
|
|
|
|
of every request. |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
Note that use of the flash is an easy way to get data across requests, but |
747
|
|
|
|
|
|
|
it's also strongly disrecommended, due it it being inherently plagued with |
748
|
|
|
|
|
|
|
race conditions. This means that it's unlikely to work well if your |
749
|
|
|
|
|
|
|
users have multiple tabs open at once, or if your site does a lot of AJAX |
750
|
|
|
|
|
|
|
requests. |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
L<Catalyst::Plugin::StatusMessage> is the recommended alternative solution, |
753
|
|
|
|
|
|
|
as this doesn't suffer from these issues. |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
sub moose : Local { |
756
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
$c->flash->{beans} = 10; |
759
|
|
|
|
|
|
|
$c->response->redirect( $c->uri_for("foo") ); |
760
|
|
|
|
|
|
|
} |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
sub foo : Local { |
763
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
my $value = $c->flash->{beans}; |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
# ... |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
$c->response->redirect( $c->uri_for("bar") ); |
770
|
|
|
|
|
|
|
} |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
sub bar : Local { |
773
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
if ( exists $c->flash->{beans} ) { # false |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
} |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=item clear_flash |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
Zap all the keys in the flash regardless of their current state. |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
=item keep_flash @keys |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
If you want to keep a flash key for the next request too, even if it hasn't |
787
|
|
|
|
|
|
|
changed, call C<keep_flash> and pass in the keys as arguments. |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
=item delete_session REASON |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
This method is used to invalidate a session. It takes an optional parameter |
792
|
|
|
|
|
|
|
which will be saved in C<session_delete_reason> if provided. |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
NOTE: This method will B<also> delete your flash data. |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
=item session_delete_reason |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
This accessor contains a string with the reason a session was deleted. Possible |
799
|
|
|
|
|
|
|
values include: |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
=over 4 |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
=item * |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
C<address mismatch> |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
=item * |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
C<session expired> |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
=back |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
=item session_expire_key $key, $ttl |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
Mark a key to expire at a certain time (only useful when shorter than the |
816
|
|
|
|
|
|
|
expiry time for the whole session). |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
For example: |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
__PACKAGE__->config('Plugin::Session' => { expires => 10000000000 }); # "forever" |
821
|
|
|
|
|
|
|
(NB If this number is too large, Y2K38 breakage could result.) |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
# later |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
$c->session_expire_key( __user => 3600 ); |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
Will make the session data survive, but the user will still be logged out after |
828
|
|
|
|
|
|
|
an hour. |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
Note that these values are not auto extended. |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
=item change_session_id |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
By calling this method you can force a session id change while keeping all |
835
|
|
|
|
|
|
|
session data. This method might come handy when you are paranoid about some |
836
|
|
|
|
|
|
|
advanced variations of session fixation attack. |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
If you want to prevent this session fixation scenario: |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
0) let us have WebApp with anonymous and authenticated parts |
841
|
|
|
|
|
|
|
1) a hacker goes to vulnerable WebApp and gets a real sessionid, |
842
|
|
|
|
|
|
|
just by browsing anonymous part of WebApp |
843
|
|
|
|
|
|
|
2) the hacker inserts (somehow) this values into a cookie in victim's browser |
844
|
|
|
|
|
|
|
3) after the victim logs into WebApp the hacker can enter his/her session |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
you should call change_session_id in your login controller like this: |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
if ($c->authenticate( { username => $user, password => $pass } )) { |
849
|
|
|
|
|
|
|
# login OK |
850
|
|
|
|
|
|
|
$c->change_session_id; |
851
|
|
|
|
|
|
|
... |
852
|
|
|
|
|
|
|
} else { |
853
|
|
|
|
|
|
|
# login FAILED |
854
|
|
|
|
|
|
|
... |
855
|
|
|
|
|
|
|
} |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
=item change_session_expires $expires |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
You can change the session expiration time for this session; |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
$c->change_session_expires( 4000 ); |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
Note that this only works to set the session longer than the config setting. |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
=back |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
=head1 INTERNAL METHODS |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
=over 4 |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=item setup |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
This method is extended to also make calls to |
874
|
|
|
|
|
|
|
C<check_session_plugin_requirements> and C<setup_session>. |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
=item check_session_plugin_requirements |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
This method ensures that a State and a Store plugin are also in use by the |
879
|
|
|
|
|
|
|
application. |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
=item setup_session |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
This method populates C<< $c->config('Plugin::Session') >> with the default values |
884
|
|
|
|
|
|
|
listed in L</CONFIGURATION>. |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
=item prepare_action |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
This method is extended. |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
Its only effect is if the (off by default) C<flash_to_stash> configuration |
891
|
|
|
|
|
|
|
parameter is on - then it will copy the contents of the flash to the stash at |
892
|
|
|
|
|
|
|
prepare time. |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
=item finalize_headers |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
This method is extended and will extend the expiry time before sending |
897
|
|
|
|
|
|
|
the response. |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
=item finalize_body |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
This method is extended and will call finalize_session before the other |
902
|
|
|
|
|
|
|
finalize_body methods run. Here we persist the session data if a session exists. |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=item initialize_session_data |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
This method will initialize the internal structure of the session, and is |
907
|
|
|
|
|
|
|
called by the C<session> method if appropriate. |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
=item create_session_id |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
Creates a new session ID using C<generate_session_id> if there is no session ID |
912
|
|
|
|
|
|
|
yet. |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
=item validate_session_id SID |
915
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
Make sure a session ID is of the right format. |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
This currently ensures that the session ID string is any amount of case |
919
|
|
|
|
|
|
|
insensitive hexadecimal characters. |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
=item generate_session_id |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
This method will return a string that can be used as a session ID. It is |
924
|
|
|
|
|
|
|
supposed to be a reasonably random string with enough bits to prevent |
925
|
|
|
|
|
|
|
collision. It basically takes C<session_hash_seed> and hashes it using SHA-1, |
926
|
|
|
|
|
|
|
MD5 or SHA-256, depending on the availability of these modules. |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
=item session_hash_seed |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
This method is actually rather internal to generate_session_id, but should be |
931
|
|
|
|
|
|
|
overridable in case you want to provide more random data. |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
Currently it returns a concatenated string which contains: |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
=over 4 |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
=item * A counter |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
=item * The current time |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
=item * One value from C<rand>. |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
=item * The stringified value of a newly allocated hash reference |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
=item * The stringified value of the Catalyst context object |
946
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
=back |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
in the hopes that those combined values are entropic enough for most uses. If |
950
|
|
|
|
|
|
|
this is not the case you can replace C<session_hash_seed> with e.g. |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
sub session_hash_seed { |
953
|
|
|
|
|
|
|
open my $fh, "<", "/dev/random"; |
954
|
|
|
|
|
|
|
read $fh, my $bytes, 20; |
955
|
|
|
|
|
|
|
close $fh; |
956
|
|
|
|
|
|
|
return $bytes; |
957
|
|
|
|
|
|
|
} |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
Or even more directly, replace C<generate_session_id>: |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
sub generate_session_id { |
962
|
|
|
|
|
|
|
open my $fh, "<", "/dev/random"; |
963
|
|
|
|
|
|
|
read $fh, my $bytes, 20; |
964
|
|
|
|
|
|
|
close $fh; |
965
|
|
|
|
|
|
|
return unpack("H*", $bytes); |
966
|
|
|
|
|
|
|
} |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
Also have a look at L<Crypt::Random> and the various openssl bindings - these |
969
|
|
|
|
|
|
|
modules provide APIs for cryptographically secure random data. |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
=item finalize_session |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
Clean up the session during C<finalize>. |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
This clears the various accessors after saving to the store. |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
=item dump_these |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
See L<Catalyst/dump_these> - ammends the session data structure to the list of |
980
|
|
|
|
|
|
|
dumped objects if session ID is defined. |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
=item calculate_extended_session_expires |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
=item calculate_initial_session_expires |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
=item create_session_id_if_needed |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
=item delete_session_id |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
=item extend_session_expires |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
Note: this is *not* used to give an individual user a longer session. See |
994
|
|
|
|
|
|
|
'change_session_expires'. |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
=item extend_session_id |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
=item get_session_id |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
=item reset_session_expires |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
=item session_is_valid |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
=item set_session_id |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
=item initial_session_expires |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
=back |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
=head1 USING SESSIONS DURING PREPARE |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
The earliest point in time at which you may use the session data is after |
1013
|
|
|
|
|
|
|
L<Catalyst::Plugin::Session>'s C<prepare_action> has finished. |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
State plugins must set $c->session ID before C<prepare_action>, and during |
1016
|
|
|
|
|
|
|
C<prepare_action> L<Catalyst::Plugin::Session> will actually load the data from |
1017
|
|
|
|
|
|
|
the store. |
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
sub prepare_action { |
1020
|
|
|
|
|
|
|
my $c = shift; |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
# don't touch $c->session yet! |
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
$c->NEXT::prepare_action( @_ ); |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
$c->session; # this is OK |
1027
|
|
|
|
|
|
|
$c->sessionid; # this is also OK |
1028
|
|
|
|
|
|
|
} |
1029
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
=head1 CONFIGURATION |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
$c->config('Plugin::Session' => { |
1033
|
|
|
|
|
|
|
expires => 1234, |
1034
|
|
|
|
|
|
|
}); |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
All configuation parameters are provided in a hash reference under the |
1037
|
|
|
|
|
|
|
C<Plugin::Session> key in the configuration hash. |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
=over 4 |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
=item expires |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
The time-to-live of each session, expressed in seconds. Defaults to 7200 (two |
1044
|
|
|
|
|
|
|
hours). |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
=item expiry_threshold |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
Only update the session expiry time if it would otherwise expire |
1049
|
|
|
|
|
|
|
within this many seconds from now. |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
The purpose of this is to keep the session store from being updated |
1052
|
|
|
|
|
|
|
when nothing else in the session is updated. |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
Defaults to 0 (in which case, the expiration will always be updated). |
1055
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
=item verify_address |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
When true, C<< $c->request->address >> will be checked at prepare time. If it is |
1059
|
|
|
|
|
|
|
not the same as the address that initiated the session, the session is deleted. |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
Defaults to false. |
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
=item verify_user_agent |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
When true, C<< $c->request->user_agent >> will be checked at prepare time. If it |
1066
|
|
|
|
|
|
|
is not the same as the user agent that initiated the session, the session is |
1067
|
|
|
|
|
|
|
deleted. |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
Defaults to false. |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
=item flash_to_stash |
1072
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
This option makes it easier to have actions behave the same whether they were |
1074
|
|
|
|
|
|
|
forwarded to or redirected to. On prepare time it copies the contents of |
1075
|
|
|
|
|
|
|
C<flash> (if any) to the stash. |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
=back |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
=head1 SPECIAL KEYS |
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
The hash reference returned by C<< $c->session >> contains several keys which |
1082
|
|
|
|
|
|
|
are automatically set: |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
=over 4 |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
=item __expires |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
This key no longer exists. Use C<session_expires> instead. |
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
=item __updated |
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
The last time a session was saved to the store. |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
=item __created |
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
The time when the session was first created. |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
=item __address |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
The value of C<< $c->request->address >> at the time the session was created. |
1101
|
|
|
|
|
|
|
This value is only populated if C<verify_address> is true in the configuration. |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
=item __user_agent |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
The value of C<< $c->request->user_agent >> at the time the session was created. |
1106
|
|
|
|
|
|
|
This value is only populated if C<verify_user_agent> is true in the configuration. |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
=back |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
=head1 CAVEATS |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=head2 Round the Robin Proxies |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
C<verify_address> could make your site inaccessible to users who are behind |
1115
|
|
|
|
|
|
|
load balanced proxies. Some ISPs may give a different IP to each request by the |
1116
|
|
|
|
|
|
|
same client due to this type of proxying. If addresses are verified these |
1117
|
|
|
|
|
|
|
users' sessions cannot persist. |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
To let these users access your site you can either disable address verification |
1120
|
|
|
|
|
|
|
as a whole, or provide a checkbox in the login dialog that tells the server |
1121
|
|
|
|
|
|
|
that it's OK for the address of the client to change. When the server sees that |
1122
|
|
|
|
|
|
|
this box is checked it should delete the C<__address> special key from the |
1123
|
|
|
|
|
|
|
session hash when the hash is first created. |
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
=head2 Race Conditions |
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
In this day and age where cleaning detergents and Dutch football (not the |
1128
|
|
|
|
|
|
|
American kind) teams roam the plains in great numbers, requests may happen |
1129
|
|
|
|
|
|
|
simultaneously. This means that there is some risk of session data being |
1130
|
|
|
|
|
|
|
overwritten, like this: |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
=over 4 |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
=item 1. |
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
request a starts, request b starts, with the same session ID |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
=item 2. |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
session data is loaded in request a |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
=item 3. |
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
session data is loaded in request b |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
=item 4. |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
session data is changed in request a |
1149
|
|
|
|
|
|
|
|
1150
|
|
|
|
|
|
|
=item 5. |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
request a finishes, session data is updated and written to store |
1153
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
=item 6. |
1155
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
request b finishes, session data is updated and written to store, overwriting |
1157
|
|
|
|
|
|
|
changes by request a |
1158
|
|
|
|
|
|
|
|
1159
|
|
|
|
|
|
|
=back |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
For applications where any given user's session is only making one request |
1162
|
|
|
|
|
|
|
at a time this plugin should be safe enough. |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
=head1 AUTHORS |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
Andy Grundman |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
Christian Hansen |
1169
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
Yuval Kogman, C<nothingmuch@woobling.org> |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
Sebastian Riedel |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
Tomas Doran (t0m) C<bobtfish@bobtfish.net> (current maintainer) |
1175
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
Sergio Salvi |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
kmx C<kmx@volny.cz> |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
Florian Ragwitz (rafl) C<rafl@debian.org> |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
Kent Fredric (kentnl) |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
And countless other contributers from #catalyst. Thanks guys! |
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
=head1 Contributors |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
Devin Austin (dhoss) <dhoss@cpan.org> |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
Robert Rothenberg <rrwo@cpan.org> (on behalf of Foxtons Ltd.) |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
Copyright (c) 2005 the aforementioned authors. All rights |
1195
|
|
|
|
|
|
|
reserved. This program is free software; you can redistribute |
1196
|
|
|
|
|
|
|
it and/or modify it under the same terms as Perl itself. |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
=cut |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
|