File Coverage

blib/lib/Apache2/AuthAny/DB.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Apache2::AuthAny::DB;
2              
3 2     2   43485 use strict;
  2         6  
  2         350  
4              
5 2     2   4407 use DBI;
  0            
  0            
6             use Data::Dumper;
7             use Digest::MD5 qw(md5_hex);
8              
9             my $dbHandle;
10             our $VERSION = '0.201';
11              
12             sub new {
13             my $class = shift;
14             my $self = {};
15              
16             unless ($dbHandle) {
17             my $dbUser = $ENV{AUTH_ANY_DB_USER} || die "Env variable AUTH_ANY_DB_USER required";
18             my $dbPasswordFile = $ENV{AUTH_ANY_DB_PW_FILE} || die "Env variable AUTH_ANY_DB_PW_FILE required";
19             open(PWD, "<$dbPasswordFile") || die "Could not read password file, '$dbPasswordFile'. $!";
20             my $dbPassword = ;
21             close(PWD) || die "ouch $!";
22             chomp $dbPassword; #remove the trailing new line
23             die "Could not get password" unless $dbPassword;
24             my $dbName = $ENV{AUTH_ANY_DB_NAME} || die "Env variable AUTH_ANY_DB_NAME required";
25             my $db;
26             $db = $ENV{AUTH_ANY_DB} || "mysql";
27             my $dsn = "database=$dbName";
28             my $dbHost = $ENV{AUTH_ANY_DB_HOST};
29              
30             $dsn .= ";host=$dbHost" if $dbHost;
31             $dbHandle = DBI->connect("DBI:$db:$dsn", $dbUser, $dbPassword) or die "user: $dbUser, errstr: $DBI::errstr";
32             $dbHandle->do('SET CHARACTER SET utf8');
33             }
34              
35             bless ($self, $class);
36             return $self;
37             }
38              
39             sub useDB {
40             return;
41             my $self = shift;
42             my $auth_any_db = $self->{auth_any_db};
43             unless ($dbHandle->do("use $auth_any_db") ) {
44             die $dbHandle->errstr;
45             }
46             }
47              
48             sub getValidRoles {
49             my $self = shift;
50             $self->useDB();
51             return $dbHandle->selectcol_arrayref('SELECT DISTINCT role FROM userRole');
52             }
53              
54             sub getUserCookieByPID {
55             my $self = shift;
56             $self->useDB();
57             my $pid = shift;
58             return unless $pid;
59             my $getCookieSql = 'select * from userAACookie where PID = ? limit 1';
60              
61             my $res = $dbHandle->selectrow_hashref($getCookieSql, undef, $pid);
62              
63             if ($res) {
64             return $res;
65             } elsif ($dbHandle->errstr) {
66             die $dbHandle->errstr;
67             } else {
68             warn "DB entry for PID cookie, '$pid' missing";
69             return;
70             }
71             }
72              
73             sub getUserByUID {
74             my $self = shift;
75             $self->useDB();
76             my ($UID) = @_;
77             my $SQL = 'SELECT * FROM user WHERE UID = ?';
78              
79             return $dbHandle->selectrow_hashref($SQL, undef, $UID);
80             }
81              
82             sub searchUsers {
83             my $self = shift;
84             $self->useDB();
85             my %usernames;
86             my ($u, $r, $n, $ident) = @_;
87             my %user = %$u;
88             my @role = @$r;
89             my @norole = @$n;
90              
91             # username must be found in each query (AND) to be listed
92             my $queries = 0;
93             if ($user{username} || $user{lastName} || $user{firstName} || $user{organization} ) {
94             $queries++;
95             my @where;
96             my @val;
97             if ($user{username}) {
98             push @where, "username LIKE ?";
99             push @val, "%$user{username}%";
100              
101             }
102             if ($user{lastName}) {
103             push @where, "lastName LIKE ?";
104             push @val, "%$user{lastName}%";
105              
106             }
107              
108             if ($user{firstName}) {
109             push @where, "firstName LIKE ?";
110             push @val, "%$user{firstName}%";
111              
112             }
113             if ($user{organization}) {
114             push @where, "organization LIKE ?";
115             push @val, "%$user{organization}%";
116              
117             }
118             my $where_clause = join " OR ", @where;
119              
120             my $SQL = "SELECT username FROM user WHERE $where_clause";
121             my $unames = $dbHandle->selectcol_arrayref($SQL, undef, @val);
122             foreach my $n (@$unames) {
123             $usernames{$n}++;
124             }
125             }
126              
127             foreach my $role (@role) {
128             $queries++;
129             my $SQL = 'SELECT username FROM user, userRole
130             WHERE user.UID = userRole.UID
131             AND role = ?';
132             my $unames = $dbHandle->selectcol_arrayref($SQL, undef, $role);
133             foreach my $n (@$unames) {
134             $usernames{$n}++;
135             }
136             }
137              
138             foreach my $role (@norole) {
139             $queries++;
140             my $SQL = 'SELECT username
141             FROM user
142             LEFT JOIN userRole ON user.UID = userRole.UID AND role = ?
143             WHERE role IS NULL';
144             my $unames = $dbHandle->selectcol_arrayref($SQL, undef, $role);
145             foreach my $n (@$unames) {
146             $usernames{$n}++;
147             }
148             }
149              
150             if ($ident) {
151             $queries++;
152             my $SQL = 'SELECT username FROM user, userIdent
153             WHERE user.UID = userIdent.UID
154             AND userIdent.authId LIKE ?';
155             my $unames = $dbHandle->selectcol_arrayref($SQL, undef, "%$ident%");
156             my %identUsers;
157             foreach my $n (@$unames) {
158             $identUsers{$n}++;
159             }
160             foreach my $n (keys %identUsers) {
161             $usernames{$n}++;
162             }
163             }
164              
165             # each query must find the name in order to return the name
166             my @usernames;
167             foreach my $n (keys %usernames) {
168             push @usernames, $n if $usernames{$n} == $queries;
169             }
170              
171             return \@usernames;
172             }
173              
174             sub getUserByUsername {
175             my $self = shift;
176             my ($username) = @_;
177             my $SQL = 'SELECT * FROM user WHERE username = ?';
178              
179             $self->useDB();
180             return $dbHandle->selectrow_hashref($SQL, undef, $username);
181             }
182              
183             sub getUserByAuthIdAndProvider {
184              
185             my $self = shift;
186             my ($authId, $authProvider) = @_;
187             return unless $authId && $authProvider;
188              
189             my $getUserSql = 'select a.* from user a, userIdent b WHERE a.UID = b.UID
190             AND authId = ? AND authProvider = ? limit 1';
191              
192             $self->useDB();
193             return $dbHandle->selectrow_hashref($getUserSql, undef, $authId, $authProvider);
194             }
195              
196             sub getBasicUser {
197             my $self = shift;
198             my ($user) = @_;
199              
200             my $sql = 'select user, password from basicAuth WHERE user = ?';
201              
202             $self->useDB();
203             return $dbHandle->selectrow_hashref($sql, undef, $user);
204             }
205              
206             sub getUserRoles {
207             my $self = shift;
208             my ($UID) = @_;
209              
210             my $getRoleSql = 'select role from userRole WHERE UID = ?';
211              
212             $self->useDB();
213             return $dbHandle->selectcol_arrayref($getRoleSql, undef, $UID);
214             }
215              
216             sub getUserRoleChoices {
217             my $self = shift;
218             my ($UID) = @_;
219              
220             my $getRoleSql = 'select role from userRoleChoice WHERE UID = ?';
221              
222             $self->useDB();
223             return $dbHandle->selectcol_arrayref($getRoleSql, undef, $UID);
224             }
225              
226             sub getUserIdentities {
227             my $self = shift;
228             my ($UID) = @_;
229              
230             my $sql = 'SELECT * FROM userIdent WHERE UID = ?';
231              
232             $self->useDB();
233             return $dbHandle->selectall_arrayref($sql, { Slice => {} }, $UID);
234             }
235              
236             sub getUserTiers {
237             my $self = shift;
238             my ($UID) = @_;
239              
240             my $getTierSql = 'select tier from userTier WHERE UID = ?';
241              
242             $self->useDB();
243             return $dbHandle->selectall_arrayref($getTierSql, { Slice => {} }, $UID);
244             }
245              
246             sub addUser {
247             my $self = shift;
248             my %user = @_;
249              
250             my @valid_cols = qw[username firstName lastName active];
251             my @cols;
252             my @passed_values;
253             my @values;
254             foreach my $col (@valid_cols) {
255             if (exists $user{$col}) {
256             push @cols, $col;
257             push @passed_values, $user{$col};
258             push @values, '?';
259             }
260             }
261             push @cols, 'created';
262             push @values, 'now()';
263              
264             my $col_list = join(",", @cols);
265             my $value_list = join(",", @values);
266             my $sql = "INSERT INTO user ($col_list) VALUES ($value_list)";
267              
268             $self->useDB();
269             if ( $dbHandle->do($sql, undef, @passed_values) ) {
270             my $UID = $dbHandle->last_insert_id(undef, undef, undef, undef);
271             return $UID;
272             } else {
273             warn $dbHandle->errstr;
274             return undef;
275             }
276             }
277              
278             sub updateUser {
279             my $self = shift;
280             my %user = @_;
281              
282             my $existingUser = $self->getUserByUsername($user{username}) || {};
283             my $UID = $existingUser->{UID};
284             unless ($UID) {
285             warn "User, '$user{username}' does not exists\n";
286             return;
287             }
288              
289             my @valid_cols = qw[firstName lastName active];
290             my @sets;
291             my @passed_values;
292              
293             foreach my $col (@valid_cols) {
294             if (exists $user{$col}) {
295             push @sets, "$col = ?";
296             push @passed_values, $user{$col};
297             }
298             }
299              
300             my $set_list = join(",", @sets);
301              
302             my $sql = "UPDATE user SET $set_list WHERE UID = ?";
303              
304             $self->useDB();
305             if ( $dbHandle->do($sql, undef, @passed_values, $UID) ) {
306             return $UID;
307             } else {
308             warn $dbHandle->errstr;
309             return undef;
310             }
311             }
312              
313             sub addUserIdent {
314             my $self = shift;
315             my ($UID, $authId, $authProvider) = @_;
316             # if (
317             # $UID !~ /^(\d+)$/
318             # || $authId !~ /^(.+)$/
319             # || $authProvider !~ /^(uw|basic|openid|protectnet|google|ldap)$/) {
320             # warn "bad input, '@_'";
321             # return;
322             # }
323              
324             # Make sure there is a user with $UID
325             # This would not be necessary if we were using tables with foreign keys
326             unless ($self->getUserByUID($UID)) {
327             warn "UID, '$UID' not found in user table";
328             return;
329             }
330              
331             # make sure $authId and $authProvider do not already exist
332             # A composite index in the DB should assure this, however we are using MyISAM
333             # TODO: check that authId/authProvider do not exists
334              
335             my $sql = 'INSERT INTO userIdent (UID, authId, authProvider) VALUES (?, ?, ?)';
336              
337             $self->useDB();
338             if ($dbHandle->do($sql, undef, $UID, $authId, $authProvider)) {
339             return 1;
340             } else {
341             warn $dbHandle->errstr;
342             return undef;
343             }
344             }
345              
346             sub removeUserIdent {
347             my $self = shift;
348             my ($UID, $authProvider) = @_;
349             # if (
350             # $UID !~ /^(\d+)$/
351             # || $authProvider !~ /^(uw|basic|openid|protectnet|google|ldap)$/) {
352             # warn "bad input, '@_'";
353             # return;
354             # }
355              
356             # Make sure there is a user with $UID
357             # This would not be necessary if we were using tables with foreign keys
358             unless ($self->getUserByUID($UID)) {
359             warn "UID, '$UID' not found in user table";
360             return;
361             }
362              
363             my $sql = 'DELETE FROM userIdent WHERE UID = ? AND authProvider = ?';
364              
365             $self->useDB();
366             if ($dbHandle->do($sql, undef, $UID, $authProvider)) {
367             return 1;
368             } else {
369             warn $dbHandle->errstr;
370             return undef;
371             }
372             }
373              
374             sub addUserRole {
375             my $self = shift;
376             my ($UID, $role) = @_;
377             if ( $UID !~ /^(\d+)$/) {
378             warn "bad input, '@_'";
379             return;
380             }
381              
382             # Make sure there is a user with $UID
383             # This would not be necessary if we were using tables with foreign keys
384             unless ($self->getUserByUID($UID)) {
385             warn "UID, '$UID' not found in user table";
386             return;
387             }
388              
389             my $sql = 'INSERT INTO userRole (UID, role) VALUES (?, ?)';
390             my $sql2 = 'INSERT INTO userRoleChoice (UID, role) VALUES (?, ?)';
391              
392             $self->useDB();
393             $self->removeUserRole($UID, $role); # prevent duplicate role errors
394             if ( $dbHandle->do($sql, undef, $UID, $role)
395             && $dbHandle->do($sql2, undef, $UID, $role)) {
396             return 1;
397             } else {
398             warn $dbHandle->errstr;
399             return undef;
400             }
401             }
402              
403              
404             sub removeUserRole {
405             my $self = shift;
406             my ($UID, $role) = @_;
407             if ( $UID !~ /^(\d+)$/) {
408             warn "bad input, '@_'";
409             return;
410             }
411              
412             # Make sure there is a user with $UID
413             # This would not be necessary if we were using tables with foreign keys
414             unless ($self->getUserByUID($UID)) {
415             warn "UID, '$UID' not found in user table";
416             return;
417             }
418              
419             my $sql = 'DELETE FROM userRole WHERE UID = ? AND role = ?';
420             my $sql2 = 'DELETE FROM userRoleChoice WHERE UID = ? AND role = ?';
421              
422             $self->useDB();
423             if ( $dbHandle->do($sql, undef, $UID, $role)
424             && $dbHandle->do($sql2, undef, $UID, $role)) {
425             return 1;
426             } else {
427             warn $dbHandle->errstr;
428             return undef;
429             }
430             }
431              
432             sub loginPCookie {
433             my $self = shift;
434             my ($pCookie, $sCookie, $authId, $authProvider) = @_;
435             unless ($pCookie && $authId && $authProvider) {
436             warn "Missing pid, authId, or authProvider. Got input @_";
437             return 0;
438             }
439              
440             my $sql = "UPDATE userAACookie
441             SET authId = ?, authProvider = ?, SID = ?, state = ?, last = ?
442             WHERE PID = ?";
443              
444             $self->useDB();
445             if ($dbHandle->do($sql, undef,
446             $authId, $authProvider, $sCookie, 'authenticated', time, $pCookie)) {
447             return 1;
448             } else {
449             warn $dbHandle->errstr;
450             return 0;
451             }
452             }
453              
454             sub logoutPCookie {
455             my $self = shift;
456             my ($pid) = @_;
457             my $pCookie = $pid->{PID};
458             unless ($pCookie) {
459             warn "Missing pid. Got input @_";
460             return 0;
461             }
462             my $logout_key = md5_hex(time . rand);
463             my $sql = "UPDATE userAACookie SET state = ?, logoutKey = ?
464             WHERE PID = ?";
465              
466             $self->useDB();
467             if ($dbHandle->do($sql, undef, 'logged_out', $logout_key, $pCookie)) {
468             $pid->{state} = 'logged_out';
469             return 1;
470             } else {
471             warn $dbHandle->errstr;
472             return 0;
473             }
474             }
475              
476             sub statePCookie {
477             my $self = shift;
478             my ($pid, $state) = @_;
479             my $pCookie = $pid->{PID};
480             unless ($pCookie && $state) {
481             warn "Missing pid or state. Got input @_";
482             return 0;
483             }
484             my $sql = "UPDATE userAACookie SET state = ?
485             WHERE PID = ?";
486              
487             $self->useDB();
488             if ($dbHandle->do($sql, undef, $state, $pCookie)) {
489             $pid->{state} = $state;
490             return 1;
491             } else {
492             warn $dbHandle->errstr;
493             return 0;
494             }
495             }
496              
497             sub insertPCookie {
498             my $self = shift;
499             my ($pCookie, $sCookie, $logout_key) = @_;
500             unless ($pCookie && $sCookie && $logout_key) {
501             warn "Missing cookies or logout_key. Got input @_";
502             return 0;
503             }
504              
505             my $sql = "INSERT INTO userAACookie (PID, SID, logoutKey, last, created)
506             VALUES (?, ?, ?, ?, now())";
507              
508             $self->useDB();
509             if ($dbHandle->do($sql, undef, $pCookie, $sCookie, $logout_key, time)) {
510             return 1;
511             } else {
512             warn $dbHandle->errstr;
513             return 0;
514             }
515             }
516              
517             sub updatePCookieLastAccess {
518             my $self = shift;
519             my ($pCookie) = @_;
520              
521             unless ($pCookie) {
522             warn "Missing pid";
523             return 0;
524             }
525              
526             my $sql = "UPDATE userAACookie
527             SET last = ?
528             WHERE pid = ?";
529              
530             $self->useDB();
531             if ($dbHandle->do($sql, undef, time, $pCookie) eq 1) {
532             return 1;
533             } else {
534             warn "Could not update DB with PID, '$pCookie'" . $dbHandle->errstr;
535             return 0;
536             }
537             }
538              
539             sub updatePCookieLogoutKey {
540             my $self = shift;
541             my ($pCookie) = @_;
542             unless ($pCookie) {
543             warn "Missing pid";
544             return 0;
545             }
546              
547             my $sql = "UPDATE userAACookie
548             SET logoutKey = ?
549             WHERE pid = ?";
550              
551             $self->useDB();
552             my $logout_key = md5_hex(time . rand);
553             if ($dbHandle->do($sql, undef, $logout_key, $pCookie)) {
554             return 1;
555             } else {
556             warn $dbHandle->errstr;
557             return 0;
558             }
559             }
560              
561             sub cleanupCookies {
562             my $self = shift;
563             $self->useDB();
564             my $sql = qq[DELETE FROM userAACookie WHERE authId IS NULL and now() - created > 300];
565              
566             my $rc = $dbHandle->do($sql);
567             if ($rc) {
568             return $rc;
569             } else {
570             warn $dbHandle->errstr;
571             return 0;
572             }
573             }
574             1;