Andrew's Web Libraries (AWL)
Loading...
Searching...
No Matches
Session.php
1<?php
25require_once('AWLUtilities.php');
26require_once('AwlQuery.php');
27require_once('EMail.php');
28
29
36function check_temporary_passwords( $they_sent, $user_no ) {
37 $sql = 'SELECT 1 AS ok FROM tmp_password WHERE user_no = ? AND password = ? AND valid_until > current_timestamp';
38 $qry = new AwlQuery( $sql, $user_no, $they_sent );
39 if ( $qry->Exec('Session::check_temporary_passwords') ) {
40 dbg_error_log( "Login", " check_temporary_passwords: Rows = ".$qry->rows());
41 if ( $row = $qry->Fetch() ) {
42 dbg_error_log( "Login", " check_temporary_passwords: OK = $row->ok");
43 // Remove all the temporary passwords for that user...
44 $sql = 'DELETE FROM tmp_password WHERE user_no = ? ';
45 $qry = new AwlQuery( $sql, $user_no );
46 $qry->Exec('Login',__LINE__,__FILE__);
47 return true;
48 }
49 }
50 return false;
51}
52
59{
63 var $roles;
64 var $cause = '';
75 var $user_no;
76
81 var $session_id = 0;
82
87 var $username = 'guest';
88
93 var $fullname = 'Guest';
94
99 var $email = '';
100
105 var $logged_in = false;
106
113 var $just_logged_in = false;
114
119 var $last_session_start;
120
126 var $last_session_end;
145 function __construct( $sid="" )
146 {
147 global $sid, $sysname;
148
149 $this->roles = array();
150 $this->logged_in = false;
151 $this->just_logged_in = false;
152 $this->login_failed = false;
153
154 if ( $sid == "" ) {
155 if ( ! isset($_COOKIE['sid']) ) return;
156 $sid = $_COOKIE['sid'];
157 }
158
159 list( $session_id, $session_key ) = explode( ';', $sid, 2 );
160
170 if ( function_exists('local_session_sql') ) {
171 $sql = local_session_sql();
172 }
173 else {
174 $sql = "SELECT session.*, usr.* FROM session JOIN usr USING ( user_no )";
175 }
176 $sql .= " WHERE session.session_id = ? AND session.session_key = ? ORDER BY session.session_start DESC LIMIT 2";
177
178 $qry = new AwlQuery($sql, $session_id, $session_key);
179 if ( $qry->Exec('Session') && 1 == $qry->rows() ) {
180 $this->AssignSessionDetails( $qry->Fetch() );
181 $qry = new AwlQuery('UPDATE session SET session_end = current_timestamp WHERE session_id=?', $session_id);
182 $qry->Exec('Session');
183 }
184 else {
185 // Kill the existing cookie, which appears to be bogus
186 setcookie('sid', '', 0,'/');
187 $this->cause = 'ERR: Other than one session record matches. ' . $qry->rows();
188 $this->Log( "WARN: Login $this->cause" );
189 }
190 }
191
192
203 function Log( $whatever )
204 {
205 global $c;
206 deprecated('Session::Log');
207
208 $argc = func_num_args();
209 $format = func_get_arg(0);
210 if ( $argc == 1 || ($argc == 2 && func_get_arg(1) == "0" ) ) {
211 error_log( "$c->sysabbr: $format" );
212 }
213 else {
214 $args = array();
215 for( $i=1; $i < $argc; $i++ ) {
216 $args[] = func_get_arg($i);
217 }
218 error_log( "$c->sysabbr: " . vsprintf($format,$args) );
219 }
220 }
221
233 function Dbg( $whatever )
234 {
235 global $debuggroups, $c;
236 deprecated('Session::Dbg');
237
238 $argc = func_num_args();
239 $dgroup = func_get_arg(0);
240
241 if ( ! (isset($debuggroups[$dgroup]) && $debuggroups[$dgroup]) ) return;
242
243 $format = func_get_arg(1);
244 if ( $argc == 2 || ($argc == 3 && func_get_arg(2) == "0" ) ) {
245 error_log( "$c->sysabbr: DBG: $dgroup: $format" );
246 }
247 else {
248 $args = array();
249 for( $i=2; $i < $argc; $i++ ) {
250 $args[] = func_get_arg($i);
251 }
252 error_log( "$c->sysabbr: DBG: $dgroup: " . vsprintf($format,$args) );
253 }
254 }
255
264 function AllowedTo ( $whatever ) {
265 return ( $this->logged_in && isset($this->roles[$whatever]) && $this->roles[$whatever] );
266 }
267
268
272 function GetRoles () {
273 $this->roles = array();
274 $qry = new AwlQuery( 'SELECT role_name FROM role_member m join roles r ON r.role_no = m.role_no WHERE user_no = ? ', $this->user_no );
275 if ( $qry->Exec('Session::GetRoles') && $qry->rows() > 0 ) {
276 while( $role = $qry->Fetch() ) {
277 $this->roles[$role->role_name] = true;
278 }
279 }
280 }
281
282
287 function AssignSessionDetails( $u ) {
288 // Assign each field in the selected record to the object
289 foreach( $u AS $k => $v ) {
290 $this->{$k} = $v;
291 }
292
293 $date_format = ($this->date_format_type == 'E' ? 'European,ISO' : ($this->date_format_type == 'U' ? 'US,ISO' : 'ISO'));
294 $qry = new AwlQuery( 'SET DATESTYLE TO '. $date_format );
295 $qry->Exec();
296
297 $this->GetRoles();
298 $this->logged_in = true;
299 }
300
301
316 function Login( $username, $password, $authenticated = false ) {
317 global $c;
318 $rc = false;
319 dbg_error_log( "Login", " Login: Attempting login for $username" );
320 if ( isset($usr) ) unset($usr);
328 if ( !$authenticated && isset($c->authenticate_hook) && isset($c->authenticate_hook['call']) && function_exists($c->authenticate_hook['call']) ) {
339 $usr = call_user_func( $c->authenticate_hook['call'], $username, $password );
340 if ( $usr === false ) unset($usr); else $authenticated = true;
341 }
342
343 $sql = "SELECT * FROM usr WHERE lower(username) = text(?) AND active";
344 $qry = new AwlQuery( $sql, strtolower($username) );
345 if ( isset($usr) || ($qry->Exec('Login',__LINE__,__FILE__) && $qry->rows() == 1 && $usr = $qry->Fetch() ) ) {
346 $user_no = ( method_exists( $usr, 'user_no' ) ? $usr->user_no() : $usr->user_no );
347 if ( $authenticated || session_validate_password( $password, $usr->password ) || check_temporary_passwords( $password, $user_no ) ) {
348 // Now get the next session ID to create one from...
349 $qry = new AwlQuery( "SELECT nextval('session_session_id_seq')" );
350 if ( $qry->Exec('Login') && $qry->rows() == 1 ) {
351 $seq = $qry->Fetch();
352 $session_id = $seq->nextval;
353 $session_key = md5( rand(1010101,1999999999) . microtime() ); // just some random shite
354 dbg_error_log( "Login", " Login: Valid username/password for $username ($user_no)" );
355
356 // Set the last_used timestamp to match the previous login.
357 $qry = new AwlQuery('UPDATE usr SET last_used = (SELECT session_start FROM session WHERE session.user_no = ? ORDER BY session_id DESC LIMIT 1) WHERE user_no = ?;', $usr->user_no, $usr->user_no);
358 $qry->Exec('Session');
359
360 // And create a session
361 $sql = "INSERT INTO session (session_id, user_no, session_key) VALUES( ?, ?, ? )";
362 $qry = new AwlQuery( $sql, $session_id, $user_no, $session_key );
363 if ( $qry->Exec('Login') ) {
364 // Assign our session ID variable
365 $sid = "$session_id;$session_key";
366
367 // Create a cookie for the sesssion
368 setcookie('sid',$sid, 0,'/');
369 // Recognise that we have started a session now too...
370 $this->__construct($sid);
371 dbg_error_log( "Login", " Login: New session $session_id started for $username ($user_no)" );
372 $this->just_logged_in = true;
373
374 // Unset all of the submitted values, so we don't accidentally submit an unexpected form.
375 unset($_POST['username']);
376 unset($_POST['password']);
377 unset($_POST['submit']);
378 unset($_GET['submit']);
379 unset($GLOBALS['submit']);
380
381 if ( function_exists('local_session_sql') ) {
382 $sql = local_session_sql();
383 }
384 else {
385 $sql = "SELECT session.*, usr.* FROM session JOIN usr USING ( user_no )";
386 }
387 $sql .= " WHERE session.session_id = ? AND session.session_key = ? ORDER BY session.session_start DESC LIMIT 2";
388
389 $qry = new AwlQuery($sql, $session_id, $session_key);
390 if ( $qry->Exec('Session') && 1 == $qry->rows() ) {
391 $this->AssignSessionDetails( $qry->Fetch() );
392 }
393
394 $rc = true;
395 return $rc;
396 }
397 // else ...
398 $this->cause = 'ERR: Could not create new session.';
399 }
400 else {
401 $this->cause = 'ERR: Could not increment session sequence.';
402 }
403 }
404 else {
405 $c->messages[] = i18n('Invalid username or password.');
406 if ( isset($c->dbg['Login']) || isset($c->dbg['ALL']) )
407 $this->cause = 'WARN: Invalid password.';
408 else
409 $this->cause = 'WARN: Invalid username or password.';
410 }
411 }
412 else {
413 $c->messages[] = i18n('Invalid username or password.');
414 if ( isset($c->dbg['Login']) || isset($c->dbg['ALL']) )
415 $this->cause = 'WARN: Invalid username.';
416 else
417 $this->cause = 'WARN: Invalid username or password.';
418 }
419
420 $this->Log( "Login failure: $this->cause" );
421 $this->login_failed = true;
422 $rc = false;
423 return $rc;
424 }
425
426
427
433 function RenderLoginPanel() {
434 $action_target = htmlspecialchars(preg_replace('/\?logout.*$/','',$_SERVER['REQUEST_URI']));
435 dbg_error_log( "Login", " RenderLoginPanel: action_target='%s'", $action_target );
436 $userprompt = translate("User Name");
437 $pwprompt = translate("Password");
438 $gobutton = htmlspecialchars(translate("GO!"));
439 $gotitle = htmlspecialchars(translate("Enter your username and password then click here to log in."));
440 $temppwprompt = translate("If you have forgotten your password then");
441 $temppwbutton = htmlspecialchars(translate("Help! I've forgotten my password!"));
442 $temppwtitle = htmlspecialchars(translate("Enter a username, if you know it, and click here, to be e-mailed a temporary password."));
443 $html = <<<EOTEXT
444<div id="logon">
445<form action="$action_target" method="post">
446<table>
447<tr>
448<th class="prompt">$userprompt:</th>
449<td class="entry">
450<input class="text" type="text" name="username" size="12" /></td>
451</tr>
452<tr>
453<th class="prompt">$pwprompt:</th>
454<td class="entry">
455<input class="password" type="password" name="password" size="12" />
456</td>
457</tr>
458<tr>
459<th class="prompt">&nbsp;</th>
460<td class="entry">
461<input type="submit" value="$gobutton" title="$gotitle" name="submit" class="submit" />
462</td>
463</tr>
464</table>
465<p>
466$temppwprompt: <input type="submit" value="$temppwbutton" title="$temppwtitle" name="lostpass" class="submit" />
467</p>
468</form>
469</div>
470
471EOTEXT;
472 return $html;
473 }
474
475
485 function LoginRequired( $groups = "" ) {
486 global $c, $session, $page_elements;
487
488 if ( $this->logged_in && $groups == "" ) return;
489 if ( ! $this->logged_in ) {
490// $c->messages[] = i18n("You must log in to use this system.");
491 if ( function_exists("local_index_not_logged_in") ) {
492 local_index_not_logged_in();
493 }
494 else {
495 $login_html = translate( "<h1>Log On Please</h1><p>For access to the %s you should log on withthe username and password that have been issued to you.</p><p>If you would like to request access, please e-mail %s.</p>");
496 $page_content = sprintf( $login_html, $c->system_name, $c->admin_email );
497 $page_content .= $this->RenderLoginPanel();
498 if ( isset($page_elements) && gettype($page_elements) == 'array' ) {
499 $page_elements[] = $page_content;
500 @include("page-renderer.php");
501 exit(0);
502 }
503 @include("page-header.php");
504 echo $page_content;
505 @include("page-footer.php");
506 }
507 }
508 else {
509 $valid_groups = explode(",", $groups);
510 foreach( $valid_groups AS $k => $v ) {
511 if ( $this->AllowedTo($v) ) return;
512 }
513 $c->messages[] = i18n("You are not authorised to use this function.");
514 if ( isset($page_elements) && gettype($page_elements) == 'array' ) {
515 @include("page-renderer.php");
516 exit(0);
517 }
518 @include("page-header.php");
519 @include("page-footer.php");
520 }
521
522 exit;
523 }
524
525
526
535 function EmailTemporaryPassword( $username, $email_address, $body_template="" ) {
536 global $c;
537
538 $password_sent = false;
539 $where = "";
540 $params = array();
541 if ( isset($username) && $username != "" ) {
542 $where = 'WHERE active AND lower(usr.username) = :lcusername';
543 $params[':lcusername'] = strtolower($username);
544 }
545 else if ( isset($email_address) && $email_address != "" ) {
546 $where = 'WHERE active AND lower(usr.email) = :lcemail';
547 $params[':lcemail'] = strtolower($email_address);
548 }
549
550 if ( $where != '' ) {
551 if ( !isset($body_template) || $body_template == "" ) {
552 $body_template = <<<EOTEXT
553
554@@debugging@@A temporary password has been requested for @@system_name@@.
555
556Temporary Password: @@password@@
557
558This has been applied to the following usernames:
559
560@@usernames@@
561and will be valid for 24 hours.
562
563If you have any problems, please contact the system administrator.
564
565EOTEXT;
566 }
567
568 $qry = new AwlQuery( 'SELECT * FROM usr '.$where, $params );
569 $qry->Exec('Session::EmailTemporaryPassword');
570 if ( $qry->rows() > 0 ) {
571 $q2 = new AwlQuery();
572 $q2->Begin();
573
574 while ( $row = $qry->Fetch() ) {
575 $mail = new EMail( "Access to $c->system_name" );
576 $mail->SetFrom($c->admin_email );
577 $usernames = "";
578 $debug_to = "";
579 if ( isset($c->debug_email) ) {
580 $debug_to = "This e-mail would normally be sent to:\n ";
581 $mail->AddTo( "Tester <$c->debug_email>" );
582 }
583
584 $tmp_passwd = '';
585 for ( $i=0; $i < 8; $i++ ) {
586 $tmp_passwd .= substr( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ+#.-=*%@0123456789abcdefghijklmnopqrstuvwxyz', rand(0,69), 1);
587 }
588
589 $q2->QDo('INSERT INTO tmp_password (user_no, password) VALUES(?,?)', array($row->user_no, $tmp_passwd));
590 if ( isset($c->debug_email) ) {
591 $debug_to .= "$row->fullname <$row->email> ";
592 }
593 else {
594 $mail->AddTo( "$row->fullname <$row->email>" );
595 }
596 $usernames .= " $row->username\n";
597
598 if ( $mail->To() != "" ) {
599 if ( isset($c->debug_email) ) {
600 $debug_to .= "\n============================================================\n";
601 }
602 $sql .= "COMMIT;";
603 $qry = new AwlQuery( $sql );
604 $qry->Exec("Session::SendTemporaryPassword");
605 $body = str_replace( '@@system_name@@', $c->system_name, $body_template);
606 $body = str_replace( '@@password@@', $tmp_passwd, $body);
607 $body = str_replace( '@@usernames@@', $usernames, $body);
608 $body = str_replace( '@@debugging@@', $debug_to, $body);
609 $mail->SetBody($body);
610 $mail->Send();
611 $password_sent = true;
612 }
613 }
614 }
615 }
616 return $password_sent;
617 }
618
619
628 global $c, $page_elements;
629
630 $password_sent = $this->EmailTemporaryPassword( (isset($_POST['username'])?$_POST['username']:null), (isset($_POST['email_address'])?$_POST['email_address']:null) );
631
632 if ( ! $password_sent && ((isset($_POST['username']) && $_POST['username'] != "" )
633 || (isset($_POST['email_address']) && $_POST['email_address'] != "" )) ) {
634 // Username or EMail were non-null, but we didn't find that user.
635 $page_content = <<<EOTEXT
636<div id="logon">
637<h1>Unable to Reset Password</h1>
638<p>We were unable to reset your password at this time. Please contact
639<a href="mailto:$c->admin_email">$c->admin_email</a>
640to arrange for an administrator to reset your password.</p>
641<p>Thank you.</p>
642</div>
643EOTEXT;
644 }
645 else if ( $password_sent ) {
646 $page_content = <<<EOTEXT
647<div id="logon">
648<h1>Temporary Password Sent</h1>
649<p>A temporary password has been e-mailed to you. This password
650will be valid for 24 hours and you will be required to change
651your password after logging in.</p>
652<p><a href=".">Click here to return to the login page.</a></p>
653</div>
654EOTEXT;
655 }
656 else {
657 $page_content = <<<EOTEXT
658<div id="logon">
659<h1>Temporary Password</h1>
660<form action="$action_target" method="post">
661<table>
662<tr>
663<th class="prompt" style="white-space: nowrap;">Enter your User Name:</th>
664<td class="entry"><input class="text" type="text" name="username" size="12" /></td>
665</tr>
666<tr>
667<th class="prompt" style="white-space: nowrap;">Or your EMail Address:</th>
668<td class="entry"><input class="text" type="text" name="email_address" size="50" /></td>
669</tr>
670<tr>
671<th class="prompt" style="white-space: nowrap;">and click on -></th>
672<td class="entry">
673<input class="submit" type="submit" value="Send me a temporary password" alt="Enter a username, or e-mail address, and click here." name="lostpass" />
674</td>
675</tr>
676</table>
677<p>Note: If you have multiple accounts with the same e-mail address, they will <em>all</em>
678be assigned a new temporary password, but only the one(s) that you use that temporary password
679on will have the existing password invalidated.</p>
680<h2>The temporary password will only be valid for 24 hours.</h2>
681<p>You will need to log on and change your password during this time.</p>
682</form>
683</div>
684EOTEXT;
685 }
686 if ( isset($page_elements) && gettype($page_elements) == 'array' ) {
687 $page_elements[] = $page_content;
688 @include("page-renderer.php");
689 exit(0);
690 }
691 @include("page-header.php");
692 echo $page_content;
693 @include("page-footer.php");
694 exit(0);
695 }
696
697 static function _CheckLogout() {
698 if ( isset($_GET['logout']) ) {
699 dbg_error_log( "Login", ":_CheckLogout: Logging out");
700 if ( isset($_COOKIE['sid']) ) {
701 // clean up our session in the session table
702 list( $session_id, $session_key ) = explode( ';', $_COOKIE['sid'], 2 );
703 $sql = 'DELETE FROM session WHERE session_id = ? AND session_key = ?';
704 $qry = new AwlQuery( $sql, $session_id, $session_key );
705 $qry->Exec('Logout',__LINE__,__FILE__);
706 // expire sessions older than 16 hours
707 $sql = "DELETE FROM session WHERE session_end < current_timestamp - interval '16 hours'";
708 $qry = new AwlQuery( $sql );
709 $qry->Exec('Expire',__LINE__,__FILE__);
710 }
711 setcookie( 'sid', '', 0,'/');
712 unset($_COOKIE['sid']);
713 unset($GLOBALS['sid']);
714 }
715 }
716
717 function _CheckLogin() {
718 global $c;
719 if ( isset($_POST['lostpass']) ) {
720 dbg_error_log( "Login", ":_CheckLogin: User '$_POST[username]' has lost the password." );
721 $this->SendTemporaryPassword();
722 }
723 else if ( isset($_POST['username']) && isset($_POST['password']) ) {
724 $username = $_POST['username'];
725 // Try and log in if we have a username and password
726 $this->Login( $_POST['username'], $_POST['password'] );
727 @dbg_error_log( "Login", ":_CheckLogin: User %s - %s (%d) login status is %d", $username, $this->fullname, $this->user_no, $this->logged_in );
728 }
729 else if ( !isset($_COOKIE['sid']) && isset($c->authenticate_hook['server_auth_type']) ) {
733 if ( ( is_array($c->authenticate_hook['server_auth_type'])
734 && in_array( strtolower($_SERVER['AUTH_TYPE']), array_map('strtolower', $c->authenticate_hook['server_auth_type'])) )
735 ||
736 ( !is_array($c->authenticate_hook['server_auth_type'])
737 && strtolower($c->authenticate_hook['server_auth_type']) == strtolower($_SERVER['AUTH_TYPE']) )
738 ) {
739 if (isset($_SERVER["REMOTE_USER"]))
740 $this->Login($_SERVER['REMOTE_USER'], "", true); // Password will not be checked.
741 else
742 $this->Login($_SERVER['REDIRECT_REMOTE_USER'], "", true); // Password will not be checked.
743 }
744 }
745 }
746
747
754 function FormattedDate( $indate, $type='date' ) {
755 $out = "";
756 if ( preg_match( '#^\s*$#', $indate ) ) {
757 // Looks like it's empty - just return empty
758 return $indate;
759 }
760 if ( preg_match( '#^\d{1,2}[/-]\d{1,2}[/-]\d{2,4}#', $indate ) ) {
761 // Looks like it's nice already - don't screw with it!
762 return $indate;
763 }
764 $yr = substr($indate,0,4);
765 $mo = substr($indate,5,2);
766 $dy = substr($indate,8,2);
767 switch ( $this->date_format_type ) {
768 case 'U':
769 $out = sprintf( "%d/%d/%d", $mo, $dy, $yr );
770 break;
771 case 'E':
772 $out = sprintf( "%d/%d/%d", $dy, $mo, $yr );
773 break;
774 default:
775 $out = sprintf( "%d-%02d-%02d", $yr, $mo, $dy );
776 break;
777 }
778 if ( $type == 'timestamp' ) {
779 $out .= substr($indate,10,6);
780 }
781 return $out;
782 }
783
784
794 function BuildConfirmationHash( $method, $varname ) {
799 $confirmation_hash = session_salted_md5( $this->session_start.$varname.$this->session_key, "" );
800 if ( $method == 'GET' ) {
801 $confirm = $varname .'='. urlencode($confirmation_hash);
802 }
803 else {
804 $confirm = sprintf( '<input type="hidden" name="%s" value="%s">', $varname, htmlspecialchars($confirmation_hash) );
805 }
806 return $confirm;
807 }
808
809
817 function CheckConfirmationHash( $method, $varname ) {
818 if ( $method == 'GET' && isset($_GET[$varname])) {
819 $hashwegot = $_GET[$varname];
820 dbg_error_log('Session',':CheckConfirmationHash: We got "%s" from GET', $hashwegot );
821 }
822 else if ( isset($_POST[$varname]) ) {
823 $hashwegot = $_POST[$varname];
824 dbg_error_log('Session',':CheckConfirmationHash: We got "%s" from POST', $hashwegot );
825 }
826 else {
827 return false;
828 }
829
830 if ( preg_match('{^\*(.+)\*.+$}i', $hashwegot, $regs ) ) {
831 // A nicely salted md5sum like "*<salt>*<salted_md5>"
832 $salt = $regs[1];
833 dbg_error_log('Session',':CheckConfirmationHash: Salt "%s"', $salt );
834 $test_against = session_salted_md5( $this->session_start.$varname.$this->session_key, $salt ) ;
835 dbg_error_log('Session',':CheckConfirmationHash: Testing against "%s"', $test_against );
836
837 return ($hashwegot == $test_against);
838 }
839 return false;
840 }
841
842}
843
844
851if ( !isset($session) ) {
852 Session::_CheckLogout();
853 $session = new Session();
854 $session->_CheckLogin();
855}
856
Login( $username, $password, $authenticated=false)
Definition Session.php:316
__construct( $sid="")
Definition Session.php:145
GetRoles()
Definition Session.php:272
BuildConfirmationHash( $method, $varname)
Definition Session.php:794
SendTemporaryPassword()
Definition Session.php:627
EmailTemporaryPassword( $username, $email_address, $body_template="")
Definition Session.php:535
FormattedDate( $indate, $type='date')
Definition Session.php:754
AllowedTo( $whatever)
Definition Session.php:264
LoginRequired( $groups="")
Definition Session.php:485
Dbg( $whatever)
Definition Session.php:233
_CheckLogin()
Definition Session.php:717
CheckConfirmationHash( $method, $varname)
Definition Session.php:817
Log( $whatever)
Definition Session.php:203
AssignSessionDetails( $u)
Definition Session.php:287
RenderLoginPanel()
Definition Session.php:433
Definition User.php:34