File Coverage

blib/lib/Dancer/Session/ElasticSearch.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             package Dancer::Session::ElasticSearch;
2              
3 1     1   891 use strict;
  1         3  
  1         42  
4 1     1   6 use warnings;
  1         1  
  1         37  
5 1     1   17 use base 'Dancer::Session::Abstract';
  1         2  
  1         707  
6              
7             use v5.10.0;
8             use Dancer qw(:syntax);
9             use ElasticSearch;
10             use Try::Tiny;
11             use Digest::HMAC_SHA1 qw();
12              
13             our $VERSION = 1.007;
14             our $es = undef;
15             our $data = {};
16              
17             sub create {
18             my $self = __PACKAGE__->new;
19              
20             $data = {};
21              
22             my $id = $self->_es->index( data => $data )->{_id};
23              
24             $self->id( $self->_sign($id) );
25              
26             return $self;
27             }
28              
29             sub flush {
30             my $self = shift;
31              
32             my $session_data = $data->{$self->id};
33              
34             try {
35             my $id = $self->_verify( $self->id );
36             $self->_es->index( data => {%$session_data}, id => $id );
37             $data = {};
38             }
39             catch {
40             warning("Could not flush session ID ". $self->id . " - $_");
41             return;
42             };
43              
44             return $self;
45             }
46              
47             sub retrieve {
48             my ( $self, $session_id ) = @_;
49              
50             my $session_data = try {
51             # return what we have if the session is_lazy
52             return $data->{$session_id} if defined $data->{$session_id} and $self->is_lazy;
53              
54             my $id = $self->_verify($session_id);
55             my $get = $self->_es->get( id => $id, ignore_missing => 1 );
56              
57             # store data locally if we're lazy
58             my $source = defined $get ? $get->{_source} : {};
59             $data->{$session_id} = $source if $self->is_lazy;
60              
61             return $source;
62             }
63             catch {
64             warning("Could not retrieve session ID $session_id - $_");
65             return;
66             };
67              
68             $session_data->{id} = $session_id;
69              
70             return bless $session_data, __PACKAGE__;
71             }
72              
73             sub destroy {
74             my $self = shift;
75             try {
76             $self->_es->delete( id => $self->id );
77             $self->write_session_id(0);
78             delete $self->{id};
79             $data = {};
80             }
81             catch {
82             warning( "Could not delete session ID " . $self->id . " - $_" );
83             return;
84             };
85             }
86              
87             sub init { }
88              
89             sub is_lazy {
90             return setting('session_options')->{is_lazy} // 1;
91             }
92              
93             # internal methods
94              
95             sub _es {
96              
97             return $es if defined $es;
98              
99             my $settings = setting('session_options');
100              
101             $es = ElasticSearch->new( %{ $settings->{connection} } );
102             $es->use_type( $settings->{type} // 'session' );
103             $es->use_index( $settings->{index} // 'session' );
104              
105             return $es;
106              
107             }
108              
109             sub _sign {
110             my ( $self, $id ) = @_;
111              
112             my $settings = setting('session_options');
113             my $length = $settings->{signing}{length} || 10;
114              
115             my $salt = join "",
116             ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' )
117             [ map { rand 64 } ( 1 .. $length ) ];
118             my $hash = $self->_hash( $id, $salt );
119              
120             return ( $hash . $salt . $id );
121             }
122              
123             sub _verify {
124             my ( $self, $string ) = @_;
125              
126             my $settings = setting('session_options');
127             my $length = $settings->{signing}{length} || 10;
128              
129             my ( $hash, $salt, $id ) = unpack "A${length}A${length}A*", $string;
130              
131             return $hash eq $self->_hash( $id, $salt )
132             ? $id
133             : die "Session ID not verified";
134             }
135              
136             sub _hash {
137             my ( $self, $id, $salt ) = @_;
138             my $settings = setting('session_options');
139             my $secret = $settings->{signing}{secret};
140             my $length = $settings->{signing}{length} || 10;
141              
142             return
143             lc substr( Digest::HMAC_SHA1::hmac_sha1_hex( $id, $secret . $salt ),
144             0, $length );
145             }
146              
147             1;
148              
149             __END__