File Coverage

blib/lib/Mojolicious/Plugin/ContextAuth/DB.pm
Criterion Covered Total %
statement 146 146 100.0
branch 18 18 100.0
condition 3 3 100.0
subroutine 20 20 100.0
pod 9 9 100.0
total 196 196 100.0


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::ContextAuth::DB;
2              
3             # ABSTRACT: Main class to do the database stuff
4              
5 51     51   54048 use Mojo::Base -base, -signatures;
  51         9832619  
  51         422  
6              
7 51     51   223105 use Crypt::Eksblowfish::Bcrypt ();
  51         157415  
  51         1355  
8 51     51   22997 use Crypt::URandom ();
  51         261270  
  51         1414  
9 51     51   367 use Mojo::Util qw(secure_compare camelize);
  51         103  
  51         4628  
10 51     51   27632 use Session::Token;
  51         455107  
  51         2016  
11              
12 51     51   31048 use Mojolicious::Plugin::ContextAuth::DB::User;
  51         169  
  51         608  
13 51     51   27509 use Mojolicious::Plugin::ContextAuth::DB::Context;
  51         778  
  51         529  
14 51     51   26846 use Mojolicious::Plugin::ContextAuth::DB::Permission;
  51         158  
  51         441  
15 51     51   27262 use Mojolicious::Plugin::ContextAuth::DB::Role;
  51         166  
  51         429  
16 51     51   26124 use Mojolicious::Plugin::ContextAuth::DB::Resource;
  51         168  
  51         470  
17              
18 51     51   1823 use Carp;
  51         126  
  51         81720  
19              
20             has token => sub {
21             Session::Token->new;
22             };
23             has [qw/dsn error/];
24              
25             has session_expires => sub { 3600 };
26              
27             has dbh => sub ($self) {
28             my $dsn = $self->dsn;
29              
30             my $dbh;
31             my $name;
32             if ( $dsn =~ m{^SQLite}xi ) {
33             require Mojo::SQLite;
34             $dbh = Mojo::SQLite->new( $dsn );
35             $name = 'sqlite';
36              
37             $dbh->on(connection => sub {
38             my ($sql, $sqlite_dbh) = @_;
39             $sqlite_dbh->do('PRAGMA foreign_keys = ON');
40             });
41             }
42             elsif ( $dsn =~ m{^postgres}xi ) {
43             require Mojo::Pg;
44             $dbh = Mojo::Pg->new( $dsn );
45             $name = 'postgres';
46             }
47             elsif ( $dsn =~ m{^(?:mariadb|mysql)}xi ) {
48             require Mojo::mysql;
49             $dbh = Mojo::mysql->strict_mode( $dsn );
50             $name = 'mysql';
51             }
52             else {
53             croak 'invalid dsn, need dsn in Mojo::{Pg,SQLite,mysql} syntax';
54             }
55              
56             $dbh->migrations->from_data( __PACKAGE__, $name );
57             $dbh->auto_migrate( 1 );
58              
59             return $dbh;
60             };
61              
62 8     8 1 4894 sub login ($self, $username, $password) {
  8         16  
  8         18  
  8         14  
  8         17  
63 8         48 $self->error('');
64              
65 8 100 100     84 if ( !$username || !$password ) {
66 3         9 $self->error('Need username and password');
67 3         14 return;
68             }
69              
70 5         25 my $user = $self->dbh->db->select(
71             corbac_users => [qw/user_id username user_password/],
72             {
73             username => $username,
74             },
75             );
76              
77 5         3213 my $hash = $user->hash;
78 5 100       205 if ( !$hash ) {
79 2         8 $self->error( 'Wrong username or password');
80 2         15 return;
81             }
82              
83             my $is_equal = secure_compare(
84             Crypt::Eksblowfish::Bcrypt::bcrypt(
85             $password,
86             $hash->{user_password},
87             ),
88             $hash->{user_password},
89 3         24 );
90              
91 3 100       1082841 if ( !$is_equal ) {
92 1         13 $self->error( 'Wrong username or password');
93 1         23 return;
94             }
95              
96 2         21 my $session_id = $self->token->get;
97              
98 2         641 my $user_object = $self->get('user', $hash->{user_id});
99 2         198 $user_object->add_session( $session_id );
100              
101 2         72 return $session_id;
102             }
103              
104 3     3 1 2007 sub user_from_session ($self, $session_id) {
  3         6  
  3         4  
  3         3  
105 3         10 $self->error('');
106              
107 3 100       20 if ( !$session_id ) {
108 1         4 $self->error( "Need session id" );
109 1         5 return;
110             }
111              
112 2         7 my $user_id = $self->dbh->db->select(
113             corbac_user_sessions => [ qw/user_id/ ],
114             {
115             session_id => $session_id,
116             session_started => { '>' => time - $self->session_expires },
117             },
118             );
119              
120 2         1371 my $hash = $user_id->hash;
121              
122 2 100       53 if ( !$hash ) {
123 1         5 $self->error('No session found');
124 1         6 return;
125             }
126              
127 1         4 my $user = Mojolicious::Plugin::ContextAuth::DB::User->new( dbh => $self->dbh );
128              
129             return $user->load(
130             $hash->{user_id},
131 1         13 );
132             }
133              
134 77     77 1 7865 sub add ($self, $object, %params) {
  77         199  
  77         217  
  77         350  
  77         154  
135 77         423 $self->error('');
136              
137 77         999 my $class = 'Mojolicious::Plugin::ContextAuth::DB::' . camelize( lc $object );
138 77         2080 my $obj = $class->new( dbh => $self->dbh );
139              
140 77         2449 my $result = $obj->add( %params );
141 77 100       478 if ( !$result ) {
142 2         6 $self->error( $obj->error );
143 2         17 return;
144             }
145              
146 75         712 return $result;
147             }
148              
149 3     3 1 2949 sub delete ($self, $object, $id) {
  3         7  
  3         12  
  3         6  
  3         8  
150 3         11 $self->error('');
151            
152 3         26 my $class = 'Mojolicious::Plugin::ContextAuth::DB::' . camelize( lc $object );
153 3         51 my $obj = $class->new( dbh => $self->dbh );
154              
155 3         41 my $result = $obj->delete( $id );
156 3 100       193 if ( !$result ) {
157 1         14 $self->error( $obj->error );
158 1         16 return;
159             }
160              
161 2         15 return $result;
162             }
163              
164 4     4 1 6347 sub update ($self, $object, $id, %params) {
  4         12  
  4         9  
  4         9  
  4         18  
  4         8  
165 4         23 $self->error('');
166              
167 4         52 my $class = 'Mojolicious::Plugin::ContextAuth::DB::' . camelize( lc $object );
168 4         102 my $obj = $class->new( dbh => $self->dbh );
169              
170 4         63 my $found = $obj->load( $id );
171 4 100       261 if ( !$found ) {
172 1         5 $self->error( $obj->error );
173 1         14 return;
174             }
175              
176 3         42 my $result = $found->update( %params );
177 3 100       573 if ( !$result ) {
178 1         9 $self->error( $found->error );
179 1         15 return;
180             }
181              
182 2         31 return $result;
183             }
184              
185 11     11 1 5583 sub get ($self, $object, $id) {
  11         26  
  11         26  
  11         25  
  11         17  
186 11         51 $self->error('');
187              
188 11         114 my $class = 'Mojolicious::Plugin::ContextAuth::DB::' . camelize( lc $object );
189 11         239 my $obj = $class->new( dbh => $self->dbh );
190              
191 11         185 return $obj->load(
192             $id,
193             );
194             }
195              
196 6     6 1 6217 sub search ($self, $object, %params) {
  6         14  
  6         12  
  6         12  
  6         10  
197 6         23 $self->error('');
198              
199 6         54 my $class = 'Mojolicious::Plugin::ContextAuth::DB::' . camelize( lc $object );
200 6         109 my $obj = $class->new( dbh => $self->dbh );
201              
202 6         102 my @rows = $obj->search( %params );
203 6         19 $self->error( $obj->error );
204            
205 6         69 return @rows;
206             }
207              
208 5     5 1 3495 sub object ($self, $object) {
  5         11  
  5         12  
  5         9  
209 5         52 $self->error('');
210              
211 5         54 my $class = 'Mojolicious::Plugin::ContextAuth::DB::' . camelize( lc $object );
212 5         96 my $obj = $class->new( dbh => $self->dbh );
213              
214 5         102 return $obj;
215             }
216              
217 1     1 1 5586 sub clear_sessions ($self) {
  1         3  
  1         2  
218 1         4 $self->dbh->db->delete(
219             'corbac_user_sessions' => {
220             session_started => { '<' => time - $self->session_expires },
221             });
222             }
223              
224             1;
225              
226             =pod
227              
228             =encoding UTF-8
229              
230             =head1 NAME
231              
232             Mojolicious::Plugin::ContextAuth::DB - Main class to do the database stuff
233              
234             =head1 VERSION
235              
236             version 0.01
237              
238             =head1 SYNOPSIS
239              
240             use Mojolicious::Plugin::ContextAuth::DB;
241              
242             use Mojo::File qw(path);
243              
244             my $file = path(__FILE__)->sibling($$ . '.db')->to_string;
245              
246             my $db = Mojolicious::Plugin::ContextAuth::DB->new(
247             dsn => 'sqlite:' . $file,
248             );
249              
250             my $session_id = $db->login( 'user', 'password' );
251              
252             # later
253              
254             my $user = $db->user_from_session( $session_id );
255              
256             =head1 ATTRIBUTES
257              
258             =over 4
259              
260             =item * dsn
261              
262             =item * error
263              
264             =item * dbh
265              
266             =item * token
267              
268             =item * session_expires
269              
270             =back
271              
272             =head1 METHODS
273              
274             =head2 login
275              
276             my $session_id = $db->login( 'user', 'password' );
277              
278             Returns the session id if I and I was correct.
279              
280             =head2 user_from_session
281              
282             my $user = $db->user_from_session( $session_id );
283              
284             Returns a L object, if a user exists that is tied to the session id.
285             It returns C if no user was found.
286              
287             =head2 add
288              
289             my $user = $db->add(
290             'user',
291             user_id => '1234',
292             username => 'username',
293             user_password => '123',
294             );
295              
296             warn $db->error if !$user;
297              
298             This is a proxy for the C methods of the C modules.
299              
300             =head2 get
301              
302             my $user = $db->get(
303             'user' => '1234',
304             );
305              
306             warn $db->error if !$user;
307              
308             This is a proxy for the C methods of the C modules.
309              
310             =head2 update
311              
312             my $updated_user = $db->update(
313             'user' => '1234',
314             username => 'username',
315             user_password => '123',
316             );
317              
318             warn $db->error if !$updated_user;
319              
320             Returns the object that reflects the updated object. If the object could not be updated
321              
322             This is a proxy for the C methods of the C modules.
323              
324             =head2 delete
325              
326             my $success = $db->delete(
327             'user' => '1234',
328             );
329              
330             warn $db->error if !$success;
331              
332             It returns C<1> on success, C otherwise.
333              
334             This is a proxy for the C methods of the C modules.
335              
336             =head2 search
337              
338             =head2 clear_sessions
339              
340             $db->clear_sessions();
341              
342             Removes all sessions from the C table that are expired.
343              
344             =head2 object
345              
346             my $obj = $db->object('user');
347              
348             Returns an instance of the C
349              
350             =head1 AUTHOR
351              
352             Renee Baecker
353              
354             =head1 COPYRIGHT AND LICENSE
355              
356             This software is Copyright (c) 2020 by Renee Baecker.
357              
358             This is free software, licensed under:
359              
360             The Artistic License 2.0 (GPL Compatible)
361              
362             =cut
363              
364             __DATA__