Version 0.16.
[catagits/Catalyst-Plugin-Session-State-Cookie.git] / lib / Catalyst / Plugin / Session / State / Cookie.pm
CommitLineData
1a776a0c 1package Catalyst::Plugin::Session::State::Cookie;
679f4a58 2use Moose;
3use namespace::autoclean;
bf2bce67 4
679f4a58 5extends 'Catalyst::Plugin::Session::State';
bf2bce67 6
45c96680 7use MRO::Compat;
74586782 8use Catalyst::Utils ();
bf2bce67 9
7c0b044c 10our $VERSION = "0.16";
ea139a65 11
679f4a58 12has _deleted_session_id => ( is => 'rw' );
81eb8ebf 13
5e50008f 14sub setup_session {
20e33791 15 my $c = shift;
5e50008f 16
45c96680 17 $c->maybe::next::method(@_);
2bde9162 18
5aca1bdd 19 $c->_session_plugin_config->{cookie_name}
7022ec4c 20 ||= Catalyst::Utils::appprefix($c) . '_session';
5e50008f 21}
22
0ff18b66 23sub extend_session_id {
24 my ( $c, $sid, $expires ) = @_;
1a776a0c 25
2bde9162 26 if ( my $cookie = $c->get_session_cookie ) {
0ff18b66 27 $c->update_session_cookie( $c->make_session_cookie( $sid ) );
58730edc 28 }
db1cda22 29
45c96680 30 $c->maybe::next::method( $sid, $expires );
2bde9162 31}
32
33sub set_session_id {
34 my ( $c, $sid ) = @_;
35
36 $c->update_session_cookie( $c->make_session_cookie( $sid ) );
37
45c96680 38 return $c->maybe::next::method($sid);
db1cda22 39}
40
41sub update_session_cookie {
58730edc 42 my ( $c, $updated ) = @_;
ab649d6b 43
8bdcbb46 44 unless ( $c->cookie_is_rejecting( $updated ) ) {
5aca1bdd 45 my $cookie_name = $c->_session_plugin_config->{cookie_name};
8bdcbb46 46 $c->response->cookies->{$cookie_name} = $updated;
47 }
48}
49
50sub cookie_is_rejecting {
51 my ( $c, $cookie ) = @_;
ab649d6b 52
8bdcbb46 53 if ( $cookie->{path} ) {
91e4fe2d 54 return 1 if index '/'.$c->request->path, $cookie->{path};
8bdcbb46 55 }
ab649d6b 56
8bdcbb46 57 return 0;
db1cda22 58}
5e50008f 59
db1cda22 60sub make_session_cookie {
2bde9162 61 my ( $c, $sid, %attrs ) = @_;
58730edc 62
5aca1bdd 63 my $cfg = $c->_session_plugin_config;
58730edc 64 my $cookie = {
2bde9162 65 value => $sid,
58730edc 66 ( $cfg->{cookie_domain} ? ( domain => $cfg->{cookie_domain} ) : () ),
8bdcbb46 67 ( $cfg->{cookie_path} ? ( path => $cfg->{cookie_path} ) : () ),
df55e818 68 %attrs,
58730edc 69 };
70
2bde9162 71 unless ( exists $cookie->{expires} ) {
72 $cookie->{expires} = $c->calculate_session_cookie_expires();
73 }
1e986fd5 74
d2cf2047 75 #beware: we have to accept also the old syntax "cookie_secure = true"
76 my $sec = $cfg->{cookie_secure} || 0; # default = 0 (not set)
77 $cookie->{secure} = 1 unless ( ($sec==0) || ($sec==2) );
ab649d6b 78 $cookie->{secure} = 1 if ( ($sec==2) && $c->req->secure );
79
5aca1bdd 80 $cookie->{httponly} = $cfg->{cookie_httponly};
3c6b7451 81 $cookie->{httponly} = 1
5aca1bdd 82 unless defined $cookie->{httponly}; # default = 1 (set httponly)
fc4b9d6d 83
1e986fd5 84 return $cookie;
85}
86
2bde9162 87sub calc_expiry { # compat
88 my $c = shift;
45c96680 89 $c->maybe::next::method( @_ ) || $c->calculate_session_cookie_expires( @_ );
2bde9162 90}
91
92sub calculate_session_cookie_expires {
93 my $c = shift;
5aca1bdd 94 my $cfg = $c->_session_plugin_config;
2bde9162 95
45c96680 96 my $value = $c->maybe::next::method(@_);
1e986fd5 97 return $value if $value;
2bde9162 98
58730edc 99 if ( exists $cfg->{cookie_expires} ) {
7022ec4c 100 if ( $cfg->{cookie_expires} > 0 ) {
1e986fd5 101 return time() + $cfg->{cookie_expires};
7022ec4c 102 }
103 else {
1e986fd5 104 return undef;
7022ec4c 105 }
58730edc 106 }
107 else {
2bde9162 108 return $c->session_expires;
58730edc 109 }
bf2bce67 110}
111
2bde9162 112sub get_session_cookie {
bf2bce67 113 my $c = shift;
1a776a0c 114
5aca1bdd 115 my $cookie_name = $c->_session_plugin_config->{cookie_name};
5e50008f 116
2bde9162 117 return $c->request->cookies->{$cookie_name};
118}
119
120sub get_session_id {
121 my $c = shift;
122
ab649d6b 123 if ( !$c->_deleted_session_id and my $cookie = $c->get_session_cookie ) {
bf2bce67 124 my $sid = $cookie->value;
bf2bce67 125 $c->log->debug(qq/Found sessionid "$sid" in cookie/) if $c->debug;
2bde9162 126 return $sid if $sid;
bf2bce67 127 }
bf2bce67 128
45c96680 129 $c->maybe::next::method(@_);
2bde9162 130}
131
132sub delete_session_id {
df55e818 133 my ( $c, $sid ) = @_;
ab649d6b 134
ea139a65 135 $c->_deleted_session_id(1); # to prevent get_session_id from returning it
df55e818 136
137 $c->update_session_cookie( $c->make_session_cookie( $sid, expires => 0 ) );
138
45c96680 139 $c->maybe::next::method($sid);
bf2bce67 140}
141
1a776a0c 142__PACKAGE__
57dbf608 143
1a776a0c 144__END__
bf2bce67 145
1a776a0c 146=pod
b2f8df5e 147
1a776a0c 148=head1 NAME
bf2bce67 149
75d3560d 150Catalyst::Plugin::Session::State::Cookie - Maintain session IDs using cookies.
bf2bce67 151
1a776a0c 152=head1 SYNOPSIS
bf2bce67 153
20e33791 154 use Catalyst qw/Session Session::State::Cookie Session::Store::Foo/;
bf2bce67 155
1a776a0c 156=head1 DESCRIPTION
bf2bce67 157
1a776a0c 158In order for L<Catalyst::Plugin::Session> to work the session ID needs to be
159stored on the client, and the session data needs to be stored on the server.
bf2bce67 160
1a776a0c 161This plugin stores the session ID on the client using the cookie mechanism.
57dbf608 162
724a6173 163=head1 METHODS
164
165=over 4
166
167=item make_session_cookie
168
169Returns a hash reference with the default values for new cookies.
170
171=item update_session_cookie $hash_ref
172
173Sets the cookie based on C<cookie_name> in the response object.
174
2cfb85de 175=item calc_expiry
176
177=item calculate_session_cookie_expires
178
179=item cookie_is_rejecting
180
181=item delete_session_id
182
183=item extend_session_id
184
185=item get_session_cookie
186
187=item get_session_id
188
189=item set_session_id
190
724a6173 191=back
192
1a776a0c 193=head1 EXTENDED METHODS
58c05d1a 194
57dbf608 195=over 4
196
1a776a0c 197=item prepare_cookies
57dbf608 198
1a776a0c 199Will restore if an appropriate cookie is found.
58c05d1a 200
d52e5079 201=item finalize_cookies
58c05d1a 202
1536d9aa 203Will set a cookie called C<session> if it doesn't exist or if its value is not
19c2baa1 204the current session id.
205
206=item setup_session
207
1536d9aa 208Will set the C<cookie_name> parameter to its default value if it isn't set.
58c05d1a 209
57dbf608 210=back
58c05d1a 211
5e50008f 212=head1 CONFIGURATION
213
214=over 4
215
216=item cookie_name
217
ae33e13f 218The name of the cookie to store (defaults to C<Catalyst::Utils::apprefix($c) . '_session'>).
5e50008f 219
41b4b15c 220=item cookie_domain
221
222The name of the domain to store in the cookie (defaults to current host)
223
7022ec4c 224=item cookie_expires
225
ab649d6b 226Number of seconds from now you want to elapse before cookie will expire.
227Set to 0 to create a session cookie, ie one which will die when the
7022ec4c 228user's browser is shut down.
229
fc4b9d6d 230=item cookie_secure
231
d2cf2047 232If this attribute B<set to 0> the cookie will not have the secure flag.
233
ab649d6b 234If this attribute B<set to 1> (or true for backward compatibility) - the cookie
235send by the server to the client will got the secure flag that tells the browser
d2cf2047 236to send this cookies back to the server only via HTTPS.
237
238If this attribute B<set to 2> then the cookie will got the secure flag only if
ab649d6b 239the request that caused cookie generation was sent over https (this option is
d2cf2047 240not good if you are mixing https and http in you application).
241
242Default vaule is 0.
243
244=item cookie_httponly
245
246If this attribute B<set to 0>, the cookie will not have HTTPOnly flag.
247
ab649d6b 248If this attribute B<set to 1>, the cookie will got HTTPOnly flag that should
d2cf2047 249prevent client side Javascript accessing the cookie value - this makes some
250sort of session hijacking attacks significantly harder. Unfortunately not all
ab649d6b 251browsers support this flag (MSIE 6 SP1+, Firefox 3.0.0.6+, Opera 9.5+); if
d2cf2047 252a browser is not aware of HTTPOnly the flag will be ignored.
253
254Default value is 1.
255
256Note1: Many peole are confused by the name "HTTPOnly" - it B<does not mean>
ab649d6b 257that this cookie works only over HTTP and not over HTTPS.
d2cf2047 258
259Note2: This paramater requires Catalyst::Runtime 5.80005 otherwise is skipped.
fc4b9d6d 260
8bdcbb46 261=item cookie_path
262
263The path of the request url where cookie should be baked.
264
5e50008f 265=back
266
d6bdceb5 267For example, you could stick this in MyApp.pm:
268
5aca1bdd 269 __PACKAGE__->config( 'Plugin::Session' => {
d6bdceb5 270 cookie_domain => '.mydomain.com',
271 });
272
724a6173 273=head1 CAVEATS
db1cda22 274
275Sessions have to be created before the first write to be saved. For example:
276
277 sub action : Local {
278 my ( $self, $c ) = @_;
279 $c->res->write("foo");
280 $c->session( ... );
281 ...
282 }
283
284Will cause a session ID to not be set, because by the time a session is
285actually created the headers have already been sent to the client.
286
bf2bce67 287=head1 SEE ALSO
288
1a776a0c 289L<Catalyst>, L<Catalyst::Plugin::Session>.
bf2bce67 290
47f47da5 291=head1 AUTHORS
bf2bce67 292
8ae6d944 293Yuval Kogman E<lt>nothingmuch@woobling.orgE<gt>
294
295=head1 CONTRIBUTORS
296
47f47da5 297This module is derived from L<Catalyst::Plugin::Session::FastMmap> code, and
298has been heavily modified since.
299
d6bdceb5 300 Andrew Ford
301 Andy Grundman
302 Christian Hansen
303 Marcus Ramberg
304 Jonathan Rockway E<lt>jrockway@cpan.orgE<gt>
305 Sebastian Riedel
7c0b044c 306 Florian Ragwitz
bf2bce67 307
308=head1 COPYRIGHT
309
bab1512d 310Copyright (c) 2005, Yuval Kogman C<< <nothingmuch@woobling.org> >>
311
312=head1 LICENSE
313
bfeb5ca0 314This program is free software, you can redistribute it and/or modify it
315under the same terms as Perl itself.
bf2bce67 316
317=cut
318
3191;