forked from PerlDancer/Dancer
/
Abstract.pm
204 lines (142 loc) · 4.9 KB
/
Abstract.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package Dancer::Session::Abstract;
use strict;
use warnings;
use Carp;
use base 'Dancer::Engine';
use Dancer::Config 'setting';
use Dancer::Cookies;
use File::Spec;
__PACKAGE__->attributes('id');
# args: ($class)
# Overload this method in your session engine if you have some init stuff to do,
# such as a database connection or making sure a directory exists...
# It will be called once the session engine is loaded.
# sub init { return 1; }
# args: ($class, $id)
# receives a session id and should return a session object if found, or undef
# otherwise.
sub retrieve {
confess "retrieve not implemented";
}
# args: ($class)
# create a new empty session, flush it and return it.
sub create {
confess "create not implemented";
}
# args: ($self)
# write the (serialized) current session to the session storage
sub flush {
confess "flush not implemented";
}
# args: ($self)
# remove the session from the session storage
sub destroy {
confess "destroy not implemented";
}
# Methods below this this line should not be overloaded.
# This is the default constructor for the session object, the only mandatory
# attribute is 'id'. The whole object should be serialized by the session
# engine.
sub init {
my ($self) = @_;
$self->id(build_id());
}
# session name can be set in configuration file:
# setting session_name => 'mydancer.session';
my $SESSION_NAME = session_name();
# this method can be overwrite in any Dancer::Session::* module
sub session_name {
setting('session_name') || 'dancer.session';
}
# we try to make the best random number
# with native Perl 5 code.
# to rebuild a session id, an attacker should know:
# - the running PID of the server
# - the current timestamp of the time it was built
# - the path of the installation directory
# - guess the correct number between 0 and 1000000000
# - should be able to reproduce that 3 times
sub build_id {
my $session_id = "";
foreach my $seed (rand(1000), rand(1000), rand(1000)) {
my $c = 0;
$c += ord($_) for (split //, File::Spec->rel2abs(File::Spec->curdir));
my $current = int($seed * 1000000000) + time + $$ + $c;
$session_id .= $current;
}
return $session_id;
}
sub read_session_id {
my $c = Dancer::Cookies->cookies->{$SESSION_NAME};
return (defined $c) ? $c->value : undef;
}
sub write_session_id {
my ($class, $id) = @_;
my %cookie = (
name => $SESSION_NAME,
value => $id
);
if (my $expires = setting('session_expires')) {
$cookie{expires} =
Dancer::Cookie::_epoch_to_gmtstring(time + $expires);
}
my $c = Dancer::Cookie->new(%cookie);
Dancer::Cookies->set_cookie_object($SESSION_NAME => $c);
}
1;
__END__
=pod
=head1 NAME
Dancer::Session::Abstract - abstract class for session engine
=head1 SPEC
=over 4
=item B<role>
A Dancer::Session object represents a session engine and should provide anything
needed to manipulate a session, whatever its storing engine is.
=item B<id>
The session id will be written to a cookie, named C<dancer.session>, it is
assumed that a client must accept cookies to be able to use a session-aware
Dancer webapp.
=item B<storage engine>
When the session engine is enabled, a I<before> filter takes care to initialize
the good Dancer::Session::Engine (according to the setting C<session>).
Then, the filter looks for a cookie named C<dancer.session> in order to
I<retrieve> the current session object. If not found, a new session object is
I<created> and its id written to the cookie.
Whenever a session call is made within a route handler, the singleton
representing the current session object is modified.
After terminating the request, a I<flush> is made to the session object.
=back
=head1 DESCRIPTION
This virtual class describes how to build a session engine for Dancer. This is
done in order to allow multiple session storage with a common interface.
Any session engine must inherits from Dancer::Session::Abstract and implement
the following abstract methods.
The default session name is "dancer_session". This can be set in your config file:
setting session_name: "mydancer_session"
=head2 Abstract Methods
=over 4
=item B<retrieve($id)>
Look for a session with the given id, return the session object if found, undef
if not.
=item B<create()>
Create a new session, return the session object.
=item B<flush()>
Write the session object to the storage engine.
=item B<destroy()>
Remove the current session object from the storage engine.
=item B<session_name> (optional)
Returns a string with the name of cookie used for storing the session ID.
=back
=head2 Inherited Methods
The following methods are not supposed to be overloaded, they are generic and
should be OK for each session engine.
=over 4
=item B<build_id>
Build a new uniq id.
=item B<read_session_id>
Reads the C<dancer.session> cookie.
=item B<write_session_id>
Write the current session id to the C<dancer.session> cookie.
=back
=cut