Hey all,
Ok, firstly I appologise for the rather long code I'm posting here.. I just thought I'd post it all because bits are linked around everywhere and some bits might be needed for reference. So not all is relevant.
I've been working on this for a LONG time, constantly starting from scratch and changing it. Recently I got it working to a stage where I could move on to start on the actual page content. So in general, it does work..
I'd basically like some advice or guidance on the best way to implement the below features, unless it is possible to just alter my code.
I've added two little features, which have some bugs which I can't get my head around to fix.
The first is to disallow the user if their session has timed out. The second is to disallow the user if they're already logged in.
My session timed out code works.. but it doesn't delete "old sessions". So what I really want is old ones to be deleted so I can return all the rows in the sesions table to say "Based on the last 5 minutes, these users are logged in" kinda thing.. At the moment I'm litereally just running a script which deletes the records from the table where the "session_last_activity" unix timestamp has expired (set to a specific value, e.g. 15 minutes).. but, there's a major bug which is that it only works when one person is accessing the site. This is because when the 2nd person access the site, the code deletes the 1st users session from the database table.. so that when the 1st user checks the site again, it re-sets up the session and assumes they haven't timed out (because it can't check it). So in general, they'd never get a timed out message.
I think the problem is the method or order of methods that I'm using.. but I can't quite work out the logic of how to do it correctly in my mind.
My user already logged in code, again works in general. If an account is logged in on one computer, the same account cannot be logged in again on a 2nd computer.
But, if the user doesn't log out properly on one computer (they just close the browser), and then opens another browser - they cannot log in again because they're "already logged in", and have to wait for it to timeout.
If anyone could help me with the way to do this, I would be very grateful! As I'm stumped on the best way of doing it, to avoid the such problems. Maybe I am doing it completely wrong, I just don't know! but I thought I was on the right track because in general it works.
So thank you for any help in advance!
Anyway, this is what i have now.. it looks a lot but a lot of it can be skipped..
The main user class
<?php
if ( !defined('IN_TAP') ) {
die("Hacking attempt");
}
class User
{
private $userQuery;
private $sessionQuery;
private $username;
private $password;
private $id;
private $level;
private $loginFromForm;
private $in_groups;
public function __construct() {
// USER CHECKS
$this->getUserPassInput();
if( $this->userExists() && $this->passwordCheck() ) {
$this->failedLoginAttempts(true);
$_SESSION['tms_user'] = $this->username;
$_SESSION['tms_pass'] = $this->password;
}
else {
$this->failedLoginAttempts();
$this->logout();
$_SESSION['login_reason'] = 'invalid_password';
$_SESSION['redirect'] = $_SERVER['PHP_SELF'];
$_SESSION['preserved_get'] = $_GET;
header("Location: ".LINK_LOGIN);
exit;
}
if( $this->otherLocationLoggedIn() ) {
$_SESSION['login_reason'] = 'already_logged_in';
$this->logout();
$_SESSION['redirect'] = $_SERVER['PHP_SELF'];
$_SESSION['preserved_get'] = $_GET;
header("Location: ".LINK_LOGIN);
exit;
}
// SESSION CHECKS
if( $this->sessionIsValid() ) {
$this->updateSession();
$this->updateLastVisit();
}
else {
if( $this->username != ANON_USER
&& $_SESSION['login_reason'] != 'session_timeout'
&& $this->loginFromForm != true ) {
$_SESSION['login_reason'] = 'session_timeout';
$this->logout();
$_SESSION['redirect'] = $_SERVER['PHP_SELF'];
$_SESSION['preserved_get'] = $_GET;
header("Location: ".LINK_LOGIN);
exit;
}
}
} // end constructor
##############################################################
private function getUserPassInput() {
if( isset($_POST['login_user']) && isset($_POST['login_pass']) ) {
$this->username = $_POST['login_user'];
$this->password = encrypt($_POST['login_pass']);
$this->loginFromForm = true;
unset($_POST['login_user'], $_POST['login_pass']);
}
else if ( isset($_SESSION['tms_user']) && isset($_SESSION['tms_pass']) ) {
$this->username = $_SESSION['tms_user'];
$this->password = $_SESSION['tms_pass'];
}
else {
$this->username = ANON_USER;
$this->password = ANON_PASS;
}
$sql = sprintf("SELECT * FROM %s WHERE user = '%s'", DB_USERS, $this->username);
$this->userQuery = mysql_query($sql);
$sql = sprintf("SELECT * FROM %s WHERE session_id = '%s'", DB_USER_SESSIONS, session_id());
$this->sessionQuery = mysql_query($sql);
}
private function userExists() {
$db_username = @mysql_result($this->userQuery, 0, 'user');
if( !empty($db_username) ) {
$this->username = $db_username;
$this->id = mysql_result($this->userQuery, 0, 'id');
$this->level = mysql_result($this->userQuery, 0, 'level');
return true;
}
return false;
}
private function passwordCheck() {
$password_match = ( $this->password == @mysql_result($this->userQuery, 0, 'pass') );
return $password_match;
}
private function userSessionExists() {
return ( mysql_num_rows($this->sessionQuery) > 0 );
}
private function newUserSession() {
$sql = sprintf("INSERT INTO %s " .
" (session_id, user_id, session_start, session_last_activity, session_ip) " .
" VALUES ('%s', '%s', '%s', '%s', '%s') ",
DB_USER_SESSIONS,
session_id(),
$this->id,
time(),
time(),
$_SERVER['REMOTE_ADDR']);
return mysql_query($sql);
}
private function otherLocationLoggedIn() {
if( $this->id == -1 ) {
return false;
}
$sql = sprintf("SELECT * FROM %s WHERE user_id = '%s'", DB_USER_SESSIONS, $this->id);
$query = mysql_query($sql);
$found_other_session = false;
while( $db_session = mysql_fetch_array($query) ) {
if( $db_session['session_id'] != session_id() || $db_session['session_ip'] != $_SERVER['REMOTE_ADDR'] ) {
$found_other_session = true;
}
}
return $found_other_session;
}
private function sessionIsValid() {
$last_activity = @mysql_result($this->sessionQuery, 0, 'session_last_activity');
if ( !$this->userSessionExists() && $this->id == -1 ) {
$this->newUserSession();
return true;
}
else if ( time() <= ($last_activity + USER_TIMEOUT) ) {
return true;
}
return false;
}
private function updateSession() {
$sql = sprintf("UPDATE %s SET" .
" user_id = %s, " .
" session_last_activity = '%s' " .
" WHERE session_id = '%s' ",
DB_USER_SESSIONS,
$this->id,
time(),
session_id() );
mysql_query($sql);
}
private function updateLastVisit() {
$sql = sprintf("UPDATE %s SET last_visit = %s WHERE id = %s", DB_USERS, time(), $this->id);
mysql_query($sql);
}
private function failedLoginAttempts($reset = false) {
if( $reset == true ) {
$sql = sprintf("UPDATE %s SET login_attempts = 0 WHERE id = %s", DB_USERS, $this->id);
}
else {
$sql = sprintf("UPDATE %s SET login_attempts = %s WHERE id = %s", DB_USERS, @mysql_result($this->userQuery, 0, 'login_attempts')+1, $this->id);
}
mysql_query($sql);
}
public function logout() {
session_unregister("tms_user");
session_unregister("tms_pass");
$this->username = ANON_USER;
$this->password = ANON_PASS;
$this->id = 0;
$sql = sprintf("UPDATE %s SET" .
" user_id = 0, " .
" session_last_activity = %s " .
" WHERE session_id = '%s' ",
DB_USER_SESSIONS,
time(),
session_id());
mysql_query($sql);
}
private function getGroupsFromDB() {
$this->in_groups = array();
$group_sql = sprintf("SELECT * FROM %s WHERE user_id = %s", DB_USER_GROUP_MEMBERS, $this->id);
$group_query = mysql_query($group_sql);
while( $row = mysql_fetch_array($group_query) ) {
$this->in_groups[] = $row['group_id'];
}
}
public function getGroupsUserIsIn() {
return count($this->in_groups) > 0 ? $this->in_groups : array();
}
public function getUsername() {
return $this->username;
}
public function getPassword() {
return $this->password;
}
public function getUserID() {
return $this->id;
}
public function getUserLevel() {
return $this->level;
}
public function getUserRepTimeOffset() {
$offset_str = '';
$working_offset = explode('.', $this->getDetail('time_offset'));
if( $working_offset[0] > 0 ) {
$offset_str .= '+';
}
$offset_str .= $working_offset[0];
if( $working_offset[1] > 0 ) {
$offset_str .= ':30';
}
return $offset_str;
}
public function getDetail($detail) {
$result = @mysql_result($this->userQuery, 0, $detail);
if( $result ) {
return $result;
}
return false;
}
private function setDetail($detail, $value) {
$sql = sprintf("UPDATE %s SET %s = '%s' WHERE user = '%s'", DB_USERS, $detail, $value, $this->username);
if( @mysql_query($sql) ) {
return true;
}
return false;
}
} // EO_CLASS
?>