| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Mojolicious::Sessions; |
|
2
|
50
|
|
|
50
|
|
422
|
use Mojo::Base -base; |
|
|
50
|
|
|
|
|
163
|
|
|
|
50
|
|
|
|
|
413
|
|
|
3
|
|
|
|
|
|
|
|
|
4
|
50
|
|
|
50
|
|
450
|
use Mojo::JSON; |
|
|
50
|
|
|
|
|
150
|
|
|
|
50
|
|
|
|
|
2449
|
|
|
5
|
50
|
|
|
50
|
|
409
|
use Mojo::Util qw(b64_decode b64_encode); |
|
|
50
|
|
|
|
|
222
|
|
|
|
50
|
|
|
|
|
38561
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
has [qw(cookie_domain secure)]; |
|
8
|
|
|
|
|
|
|
has cookie_name => 'mojolicious'; |
|
9
|
|
|
|
|
|
|
has cookie_path => '/'; |
|
10
|
|
|
|
|
|
|
has default_expiration => 3600; |
|
11
|
|
|
|
|
|
|
has deserialize => sub { \&_deserialize }; |
|
12
|
|
|
|
|
|
|
has samesite => 'Lax'; |
|
13
|
|
|
|
|
|
|
has serialize => sub { \&_serialize }; |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub load { |
|
16
|
187
|
|
|
187
|
1
|
471
|
my ($self, $c) = @_; |
|
17
|
|
|
|
|
|
|
|
|
18
|
187
|
100
|
|
|
|
607
|
return unless my $value = $c->signed_cookie($self->cookie_name); |
|
19
|
31
|
|
|
|
|
164
|
$value =~ y/-/=/; |
|
20
|
31
|
50
|
|
|
|
395
|
return unless my $session = $self->deserialize->(b64_decode $value); |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# "expiration" value is inherited |
|
23
|
31
|
|
66
|
|
|
300
|
my $expiration = $session->{expiration} // $self->default_expiration; |
|
24
|
31
|
50
|
66
|
|
|
161
|
return if !(my $expires = delete $session->{expires}) && $expiration; |
|
25
|
31
|
50
|
66
|
|
|
221
|
return if defined $expires && $expires <= time; |
|
26
|
|
|
|
|
|
|
|
|
27
|
31
|
|
|
|
|
124
|
my $stash = $c->stash; |
|
28
|
31
|
100
|
|
|
|
196
|
return unless $stash->{'mojo.active_session'} = keys %$session; |
|
29
|
30
|
|
|
|
|
115
|
$stash->{'mojo.session'} = $session; |
|
30
|
30
|
100
|
|
|
|
140
|
$session->{flash} = delete $session->{new_flash} if $session->{new_flash}; |
|
31
|
|
|
|
|
|
|
} |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub store { |
|
34
|
932
|
|
|
932
|
1
|
2336
|
my ($self, $c) = @_; |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# Make sure session was active |
|
37
|
932
|
|
|
|
|
2358
|
my $stash = $c->stash; |
|
38
|
932
|
100
|
|
|
|
3637
|
return unless my $session = $stash->{'mojo.session'}; |
|
39
|
100
|
50
|
66
|
|
|
633
|
return unless keys %$session || $stash->{'mojo.active_session'}; |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# Don't reset flash for static files |
|
42
|
45
|
|
|
|
|
123
|
my $old = delete $session->{flash}; |
|
43
|
45
|
50
|
|
|
|
139
|
$session->{new_flash} = $old if $stash->{'mojo.static'}; |
|
44
|
45
|
100
|
|
|
|
109
|
delete $session->{new_flash} unless keys %{$session->{new_flash}}; |
|
|
45
|
|
|
|
|
222
|
|
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# Generate "expires" value from "expiration" if necessary |
|
47
|
45
|
|
66
|
|
|
301
|
my $expiration = $session->{expiration} // $self->default_expiration; |
|
48
|
45
|
|
|
|
|
138
|
my $default = delete $session->{expires}; |
|
49
|
45
|
100
|
66
|
|
|
346
|
$session->{expires} = $default || time + $expiration if $expiration || $default; |
|
|
|
|
66
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
51
|
45
|
|
|
|
|
214
|
my $value = b64_encode $self->serialize->($session), ''; |
|
52
|
45
|
|
|
|
|
196
|
$value =~ y/=/-/; |
|
53
|
|
|
|
|
|
|
my $options = { |
|
54
|
|
|
|
|
|
|
domain => $self->cookie_domain, |
|
55
|
|
|
|
|
|
|
expires => $session->{expires}, |
|
56
|
45
|
|
|
|
|
188
|
httponly => 1, |
|
57
|
|
|
|
|
|
|
path => $self->cookie_path, |
|
58
|
|
|
|
|
|
|
samesite => $self->samesite, |
|
59
|
|
|
|
|
|
|
secure => $self->secure |
|
60
|
|
|
|
|
|
|
}; |
|
61
|
45
|
|
|
|
|
151
|
$c->signed_cookie($self->cookie_name, $value, $options); |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
31
|
|
|
31
|
|
368
|
sub _deserialize { Mojo::JSON::decode_json($_[0] =~ s/\}\KZ*$//r) } |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub _serialize { |
|
67
|
50
|
|
|
50
|
|
509
|
no warnings 'numeric'; |
|
|
50
|
|
|
|
|
151
|
|
|
|
50
|
|
|
|
|
6871
|
|
|
68
|
45
|
|
|
45
|
|
209
|
my $out = Mojo::JSON::encode_json($_[0]); |
|
69
|
45
|
|
|
|
|
649
|
return $out . 'Z' x (1025 - length $out); |
|
70
|
|
|
|
|
|
|
} |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
1; |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=encoding utf8 |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=head1 NAME |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Mojolicious::Sessions - Session manager based on signed cookies |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
use Mojolicious::Sessions; |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
my $sessions = Mojolicious::Sessions->new; |
|
85
|
|
|
|
|
|
|
$sessions->cookie_name('myapp'); |
|
86
|
|
|
|
|
|
|
$sessions->default_expiration(86400); |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
L manages sessions based on signed cookies for L. All data gets serialized with |
|
91
|
|
|
|
|
|
|
L and stored Base64 encoded on the client-side, but is protected from unwanted changes with a HMAC-SHA256 |
|
92
|
|
|
|
|
|
|
signature. |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
L implements the following attributes. |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head2 cookie_domain |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
my $domain = $sessions->cookie_domain; |
|
101
|
|
|
|
|
|
|
$sessions = $sessions->cookie_domain('.example.com'); |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Domain for session cookies, not defined by default. |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head2 cookie_name |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
my $name = $sessions->cookie_name; |
|
108
|
|
|
|
|
|
|
$sessions = $sessions->cookie_name('session'); |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
Name for session cookies, defaults to C. |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head2 cookie_path |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
my $path = $sessions->cookie_path; |
|
115
|
|
|
|
|
|
|
$sessions = $sessions->cookie_path('/foo'); |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
Path for session cookies, defaults to C>. |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head2 default_expiration |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
my $time = $sessions->default_expiration; |
|
122
|
|
|
|
|
|
|
$sessions = $sessions->default_expiration(3600); |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Default time for sessions to expire in seconds from now, defaults to C<3600>. The expiration timeout gets refreshed for |
|
125
|
|
|
|
|
|
|
every request. Setting the value to C<0> will allow sessions to persist until the browser window is closed, this can |
|
126
|
|
|
|
|
|
|
have security implications though. For more control you can also use the C and C session values. |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Expiration date in seconds from now (persists between requests) |
|
129
|
|
|
|
|
|
|
$c->session(expiration => 604800); |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# Expiration date as absolute epoch time (only valid for one request) |
|
132
|
|
|
|
|
|
|
$c->session(expires => time + 604800); |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Delete whole session by setting an expiration date in the past |
|
135
|
|
|
|
|
|
|
$c->session(expires => 1); |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=head2 deserialize |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
my $cb = $sessions->deserialize; |
|
140
|
|
|
|
|
|
|
$sessions = $sessions->deserialize(sub ($bytes) {...}); |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
A callback used to deserialize sessions, defaults to L. |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
$sessions->deserialize(sub ($bytes) { return {} }); |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=head2 samesite |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my $samesite = $sessions->samesite; |
|
149
|
|
|
|
|
|
|
$sessions = $sessions->samesite('Strict'); |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Set the SameSite value on all session cookies, defaults to C. Note that this attribute is B because |
|
152
|
|
|
|
|
|
|
even though most commonly used browsers support the feature, there is no specification yet besides L
|
|
153
|
|
|
|
|
|
|
draft|https://tools.ietf.org/html/draft-west-first-party-cookies-07>. |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# Disable SameSite feature |
|
156
|
|
|
|
|
|
|
$sessions->samesite(undef); |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=head2 secure |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
my $bool = $sessions->secure; |
|
161
|
|
|
|
|
|
|
$sessions = $sessions->secure($bool); |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
Set the secure flag on all session cookies, so that browsers send them only over HTTPS connections. |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=head2 serialize |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
my $cb = $sessions->serialize; |
|
168
|
|
|
|
|
|
|
$sessions = $sessions->serialize(sub ($hash) {...}); |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
A callback used to serialize sessions, defaults to L. |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
$sessions->serialize(sub ($hash) { return '' }); |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=head1 METHODS |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
L inherits all methods from L and implements the following new ones. |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=head2 load |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
$sessions->load(Mojolicious::Controller->new); |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
Load session data from signed cookie. |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=head2 store |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
$sessions->store(Mojolicious::Controller->new); |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
Store session data in signed cookie. |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
L, L, L. |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=cut |