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