File Coverage

blib/lib/RapidApp/CoreSchema/Result/Session.pm
Criterion Covered Total %
statement 63 63 100.0
branch 14 22 63.6
condition 4 12 33.3
subroutine 18 18 100.0
pod 1 5 20.0
total 100 120 83.3


line stmt bran cond sub pod time code
1             package RapidApp::CoreSchema::Result::Session;
2              
3 1     1   589 use strict;
  1         2  
  1         30  
4 1     1   6 use warnings;
  1         2  
  1         29  
5              
6 1     1   5 use Moose;
  1         2  
  1         8  
7 1     1   5766 use MooseX::NonMoose;
  1         2  
  1         9  
8 1     1   4586 use namespace::autoclean;
  1         2  
  1         10  
9             extends 'DBIx::Class::Core';
10              
11             __PACKAGE__->table('session');
12              
13             __PACKAGE__->add_columns(
14             "id" => {
15             data_type => "varchar",
16             is_nullable => 0,
17             },
18             "session_data" => {
19             data_type => "text",
20             is_nullable => 1,
21             },
22             "expires" => {
23             data_type => "integer",
24             extra => { unsigned => 1 },
25             is_nullable => 1,
26             },
27             "expires_ts" => {
28             data_type => "datetime",
29             datetime_undef_if_invalid => 1,
30             is_nullable => 1,
31             },
32             "user_id" => {
33             data_type => "integer",
34             extra => { unsigned => 1 },
35             is_nullable => 1,
36             },
37             );
38              
39             __PACKAGE__->set_primary_key('id');
40              
41             __PACKAGE__->belongs_to(
42             "user",
43             "RapidApp::CoreSchema::Result::User",
44             { id => "user_id" },
45             {
46             is_deferrable => 1,
47             join_type => "LEFT",
48             on_delete => "CASCADE",
49             on_update => "CASCADE",
50             },
51             );
52              
53 1     1   195 use DateTime;
  1         3  
  1         28  
54 1     1   5 use MIME::Base64;
  1         2  
  1         58  
55 1     1   5 use Storable;
  1         2  
  1         48  
56 1     1   6 use Try::Tiny;
  1         2  
  1         95  
57              
58             sub insert {
59 8     8 1 13051 my $self = shift;
60 8         43 $self->_set_extra_columns(@_);
61 8         26 return $self->next::method;
62             }
63              
64 1     1   6 use RapidApp::Util ':all';
  1         3  
  1         1080  
65              
66             around 'update' => sub {
67             my ($orig,$self,@args) = @_;
68             $self->_set_extra_columns(@args);
69            
70             # This is terrible, but there are situations in which the session handling logic
71             # of the AuthCore + Session::Store::DBIC crazy straw will try to save a session
72             # that is not in the database, but tells dbic that it is, and tries to update it,
73             # which barfs. So, here are catching exceptions on update and trying to create
74             # as a new row instead. This situation seems to happen when attempting to
75             # authenticate during the course of another request, when there is no session but
76             # the client browser has a session cookie. This is ugly but not all that unsafe,
77             # since if update throws an exception, something is already terribly wrong
78             try {
79             $self->$orig
80             }
81             catch {
82             $self = $self->result_source->resultset->create(
83             { $self->get_columns }
84             );
85             };
86            
87             return $self
88             };
89              
90             sub _set_extra_columns {
91 30     30   65 my $self = shift;
92 30         67 my $columns = shift;
93 30 50       97 $self->set_inflated_columns($columns) if $columns;
94            
95 30         135 my $expires = $self->get_column('expires');
96 30 100       818 $self->set_column( expires_ts => DateTime->from_epoch(
97             epoch => $expires,
98             time_zone => 'local'
99             ) ) if ($expires);
100            
101 30         2217 my $data = $self->decoded_session_data;
102 30 100       1048 if($data) {
103 19     19   97 my $user_id = try{$data->{__user}{id}};
  19         384  
104 19         205 $self->set_column( user_id => $user_id );
105             }
106             }
107              
108             sub decoded_session_data {
109 31     31 0 71 my $self = shift;
110 31 100       97 my $value = $self->get_column('session_data') or return undef;
111 20     20   402 return try{ Storable::thaw(MIME::Base64::decode($value)) };
  20         555  
112             }
113              
114              
115             sub encode_set_session_data {
116 1     1 0 3 my $self = shift;
117 1         3 my $data = shift;
118            
119 1 50 33     7 die "encode_set_session_data(): first argument must be a HashRef"
120             unless ($data && ref($data) eq 'HASH');
121            
122 1 50       6 $self->session_data( MIME::Base64::encode(Storable::nfreeze($data)) ) && return $self
123             }
124              
125             sub set_encoded_session_keys {
126 1     1 0 2 my $self = shift;
127 1         3 my $new = shift;
128            
129 1 50 33     14 die "set_encoded_session_keys(): first argument must be a HashRef"
130             unless ($new && ref($new) eq 'HASH');
131            
132 1 50       7 my $data = $self->decoded_session_data or die "Failed to get current encoded session data";
133            
134 1 50       54 $self->encode_set_session_data({ %$data, %$new }) && return $self
135             }
136              
137             sub set_expires {
138 1     1 0 2304 my $self = shift;
139 1         3 my $epoch = shift;
140 1 50       4 die "set_expires(): requires valid unix epoch argument" unless (defined $epoch);
141 1 50 33     14 die "set_expires(): supplied value '$epoch' is not a valid unix epoch" unless (
      33        
142             ($epoch =~ /^\d+$/) && $epoch >= 0 && $epoch < 2**31
143             );
144            
145 1         8 $self->set_encoded_session_keys({ __expires => $epoch });
146 1         127 $self->expires($epoch);
147 1         130 return $self
148             }
149              
150              
151             __PACKAGE__->load_components('+RapidApp::DBIC::Component::TableSpec');
152             __PACKAGE__->add_virtual_columns(
153             expires_in => {
154             data_type => "integer",
155             is_nullable => 1,
156             sql => sub {
157             # this is exactly the same method (with time()) how
158             # Catalyst::Plugin::Session::Store::DBIC is checking
159             # the session
160             'SELECT (self.expires - '.(time()).')'
161             },
162             },
163             );
164             __PACKAGE__->apply_TableSpec;
165              
166             __PACKAGE__->TableSpec_set_conf(
167             title => 'Session',
168             title_multi => 'Sessions',
169             iconCls => 'ra-icon-environment-network',
170             multiIconCls => 'ra-icon-environment-network',
171             display_column => 'id',
172             priority_rel_columns => 1,
173             columns => {
174             id => {
175             width => 300,
176             allow_add => \0, allow_edit => \0,
177             },
178             session_data => {
179             hidden => \1,
180             renderer => 'Ext.ux.RapidApp.renderBase64'
181             },
182             user_id => { no_column => \1, no_multifilter => \1, no_quick_search => \1 },
183             expires => {
184             width => 100, hidden => \1,
185             allow_add => \0, allow_edit => \0,
186             },
187             expires_ts => {
188             allow_add => \0, allow_edit => \0,
189             width => 130
190             },
191             expires_in => {
192             width => 100,
193             renderer => 'Ext.ux.RapidApp.renderSecondsElapsed'
194             },
195             user => { allow_add => \0, allow_edit => \0 },
196             }
197             );
198              
199             __PACKAGE__->meta->make_immutable;
200             1;