File Coverage

blib/lib/RapidApp/CoreSchema/Result/Session.pm
Criterion Covered Total %
statement 47 47 100.0
branch 7 8 87.5
condition n/a
subroutine 15 15 100.0
pod 1 2 50.0
total 70 72 97.2


line stmt bran cond sub pod time code
1             package RapidApp::CoreSchema::Result::Session;
2              
3 1     1   642 use strict;
  1         2  
  1         30  
4 1     1   4 use warnings;
  1         3  
  1         28  
5              
6 1     1   5 use Moose;
  1         2  
  1         8  
7 1     1   5977 use MooseX::NonMoose;
  1         2  
  1         8  
8 1     1   4702 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   161 use DateTime;
  1         2  
  1         20  
54 1     1   4 use MIME::Base64;
  1         2  
  1         72  
55 1     1   6 use Storable;
  1         2  
  1         48  
56 1     1   8 use Try::Tiny;
  1         1  
  1         99  
57              
58             sub insert {
59 8     8 1 13466 my $self = shift;
60 8         54 $self->_set_extra_columns(@_);
61 8         27 return $self->next::method;
62             }
63              
64 1     1   7 use RapidApp::Util ':all';
  1         2  
  1         926  
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 29     29   70 my $self = shift;
92 29         67 my $columns = shift;
93 29 50       118 $self->set_inflated_columns($columns) if $columns;
94            
95 29         137 my $expires = $self->get_column('expires');
96 29 100       826 $self->set_column( expires_ts => DateTime->from_epoch(
97             epoch => $expires,
98             time_zone => 'local'
99             ) ) if ($expires);
100            
101 29         2273 my $data = $self->decoded_session_data;
102 29 100       1080 if($data) {
103 18     18   104 my $user_id = try{$data->{__user}{id}};
  18         359  
104 18         210 $self->set_column( user_id => $user_id );
105             }
106             }
107              
108             sub decoded_session_data {
109 29     29 0 72 my $self = shift;
110 29 100       118 my $value = $self->get_column('session_data') or return undef;
111 18     18   372 return try{ Storable::thaw(MIME::Base64::decode($value)) };
  18         505  
112             }
113              
114             __PACKAGE__->load_components('+RapidApp::DBIC::Component::TableSpec');
115             __PACKAGE__->add_virtual_columns(
116             expires_in => {
117             data_type => "integer",
118             is_nullable => 1,
119             sql => sub {
120             # this is exactly the same method (with time()) how
121             # Catalyst::Plugin::Session::Store::DBIC is checking
122             # the session
123             'SELECT (self.expires - '.(time()).')'
124             },
125             },
126             );
127             __PACKAGE__->apply_TableSpec;
128              
129             __PACKAGE__->TableSpec_set_conf(
130             title => 'Session',
131             title_multi => 'Sessions',
132             iconCls => 'ra-icon-environment-network',
133             multiIconCls => 'ra-icon-environment-network',
134             display_column => 'id',
135             priority_rel_columns => 1,
136             columns => {
137             id => {
138             width => 300,
139             allow_add => \0, allow_edit => \0,
140             },
141             session_data => {
142             hidden => \1,
143             renderer => 'Ext.ux.RapidApp.renderBase64'
144             },
145             user_id => { no_column => \1, no_multifilter => \1, no_quick_search => \1 },
146             expires => {
147             width => 100, hidden => \1,
148             allow_add => \0, allow_edit => \0,
149             },
150             expires_ts => {
151             allow_add => \0, allow_edit => \0,
152             width => 130
153             },
154             expires_in => {
155             width => 100,
156             renderer => 'Ext.ux.RapidApp.renderSecondsElapsed'
157             },
158             user => { allow_add => \0, allow_edit => \0 },
159             }
160             );
161              
162             __PACKAGE__->meta->make_immutable;
163             1;