PDA

View Full Version : Inactive User Auto-Logout


kendall
05-21-2008, 11:33 PM
This tutorial is made to show you how to make a system to automatically log off users after a certain amount of time and has other features like showing the currently logged on users and with a little work you could even make the system forcible kick out and log out a user. This is meant to teach you how to implement this kind of system to an already running user management system. The small user management system I made for this is not very good or meant to really be used for much then learning in this tutorial. I am telling you this because I will not explain some of those parts and my user management system has a mayjor flaw![ Someone could inject a sql statement with nasty stuff, can you see where?] By the way, I will assume you can add tables to a database, use/understand mysql functions in php and generally know the basic syntax of php.

That being said user system I built for these examples can be downloaded from the attachment I have provided.

First thing is first so we will need to make a new table in our database that we will use to monitor who is online. We will call this table online, you can call ours something else but remember I'm calling it online and that is what all of my code says. The table will just have a ID field and a TTL field, Time To Live. Here is a sql statement to make a table.
CREATE TABLE `online` (
`ID` longtext NOT NULL,
`TTL` mediumtext NOT NULL
)

Your current user management system should already have a field in your users table called something like ID,which is what I call mine, that gives a unique numerical ID to each and everyone of your users. If you don't then you can still do this but I suggest you can your user system to issue IDs instead. Giving an unique ID to each user can help increase security and even lower bandwidth. With an unique ID you don't have to worry about deleting or editing the wrong record, reading the wrong record,conflicts were two users have the same user name, and there is less of an chance of some one managing to inject some nasty stuff into your sql statements.

Back to the subject, we need to make it so when the user logs into there account the system will add a record to the online table for them. Here is the code I used:
function logIn($page)
{
if(!isset($_POST['LogInForm']))
{
logOut();
$file=findFile("logIn.php");
if(!$file)
{

exit("<br /><b>Fatel Error: Missing required file.</b>");
}
else
{
echo "<center><b><font color='blue'><h1>LogIn</h1></font></b></center><br />";
include($file);
printLogInPage($page);
exit;
}
}
else
{
if(empty($_POST['email'])||empty($_POST['pass']))
{
logOut();
header("Location: http://".$_SERVER['HTTP_HOST'].$page."?logIn");
exit;
}
$email=$_POST['email'];
$password=sha1(sha1(md5($_POST['pass']).md5($email).crc32($email)));
$sql="SELECT username,ID FROM users WHERE email='".$email."' AND password='".$password."';";
$result=mysql_query($sql) or die("Database Error.");
while($row=mysql_fetch_assoc($result))
{
$username=$row['username'];
$ID=$row['ID'];
if(isset($ran)) return false;//Something is very wrong because there should only be one record, so don't login
}
if(empty($ID))
{
logOut();
exit('Failed: wrong email and or password.');
}
$_SESSION['ID']=$ID;
$_SESSION['password']=$password;
$_SESSION['username']=$username;//just for convence so we don't have to make more DB calls.
$ttl=time()+(15*60)+1;//the current time plus 15 minutes plus one second so they have a full 15 minutes.
$sql="INSERT INTO online (`ID`,`TTL`) VALUES ('".$ID."', '".$ttl."')";
mysql_query($sql) or die("Database Error.");
header("Location: http://".$_SERVER['HTTP_HOST'].$page);
echo "You have been logged in. PLease what while you are redirected.";
exit;
}
}

I know that might look long and scary but most of it is just the stuff to login the user, I will be just taking about the parts that add the user to the online table. Ok first is first, when the user logs in with what ever way they do in your system, the system need to get their ID.

$ttl=time()+(15*60)+1;//the current time plus 15 minutes plus one second so they have a full 15 minutes.

What we are doing here is getting the current time with the function time(), then we are adding 15 minutes but the time function gave us the time in seconds so we have to multiply 15 by 60. Finally we add one second so they get a full 15 minutes of inactivitie.

$sql="INSERT INTO online (`ID`,`TTL`) VALUES ('".$ID."', '".$ttl."')";
mysql_query($sql) or die("Database Error.");

Now we are putting that ID we got earlier in a sql statement with the $ttl. Ok, we are getting to the good parts now! Each time the user goes to any page you need to make sure you update their time so they don't get logged out and check up on all the online people. We first need to check to see if anyone is over the time limit. First thing we need to do is add a recod to our online table that will will not need to delete again as we will just be editing it from now on. This record is meant to hold the time that out script ,Server, is going to clean the table.

INSERT INTO `online` VALUES ('Server', '0');

Putting 0 will ensure that it will update the first time it is used.

function cleaner()
{
$time=time();//our time
$sql="SELECT TTL FROM online WHERE ID='Server' LIMIT 1;";
$result=mysql_query($sql) or die("Databas Error.");
$ttl=mysql_fetch_assoc($result);
$ttl=$ttl['TTL'];
if($ttl<=$time)
{
$sql="SELECT ID,TTL FROM online WHERE ID!='Server';";
$result=mysql_query($sql) or die("Databas Error.");
while($row=mysql_fetch_assoc($result))
{
if($row['TTL']<=$time)
{
$sql="DELETE FROM online Where ID='".$row['ID']."' LIMIT 1;";
mysql_query($sql) or die("Databas Error.");
}
}
$ttl=time()+(15*60)+2;
$sql="UPDATE `online` SET TTL='".$ttl."' WHERE ID='Server' LIMIT 1;";
mysql_query($sql) or die("Databas Error.");
}

}

ok this first part in our cleaner function is just getting what time it is then getting when the server is to clean the online table.

$time=time();//the current time
$sql="SELECT TTL FROM online WHERE ID='Server' LIMIT 1;";//the sql that will get what time this needs to really run.
$result=mysql_query($sql) or die("Databas Error.");
$ttl=mysql_fetch_assoc($result);
$ttl=$ttl['TTL'];//the time this is really going to run

Our next part compares the time that the record says it is suppost to update and the current time and if it has been past the time recorded already then we run the junk that makes this work.

if($ttl<=$time)
{
$sql="SELECT ID,TTL FROM online WHERE ID!='Server';";
$result=mysql_query($sql) or die("Databas Error.");
while($row=mysql_fetch_assoc($result))
{
if($row['TTL']<=$time)
{
$sql="DELETE FROM online Where ID='".$row['ID']."' LIMIT 1;";
mysql_query($sql) or die("Databas Error.");
}
}
$ttl=time()+(15*60)+2;
$sql="UPDATE `online` SET TTL='".$ttl."' WHERE ID='Server' LIMIT 1;";
mysql_query($sql) or die("Databas Error.");
}

In this next part we are getting the ID and TTL of everyone except the Server.

$sql="SELECT ID,TTL FROM online WHERE ID!='Server';";
$result=mysql_query($sql) or die("Databas Error.");

Now we need to go though each one of these and if it has been past time for their TTl will delete them.

while($row=mysql_fetch_assoc($result))
{
if($row['TTL']<=$time)//same as before
{
$sql="DELETE FROM online Where ID='".$row['ID']."' LIMIT 1;";//The deleting based on ID
mysql_query($sql) or die("Databas Error.");
}
}

Now that the server has cleaned up the online table we need to update the time it cleans next so that we don't waste bandwitdh on every call of this script.

$ttl=time()+(15*60)+2;
$sql="UPDATE `online` SET TTL='".$ttl."' WHERE ID='Server' LIMIT 1;";
mysql_query($sql) or die("Databas Error.");

I set the server's ttl to the current time then 15 minutes and finally plus two seconds that if the user that is running now comes back late when they refresh it will clean and log them out too!

Now for the key stuff that make this useful. After when check to see if we need to clean the tables we should update the user time so that they can keep logged in.

$sql="UPDATE `online` SET TTL='".(time()+(15*60)+1)."' WHERE ID='".$_SESSION['ID']."' LIMIT 1;";
mysql_query($sql) or die("Databas Error.");

Now on every page you should already be checking if a user is logged in and we will tie into this system to finish these final parts. When you go to check to see if the password and ID matches a user you need to see if they are still in logged in. So we will use something simple to do this:


$sql="SELECT * FROM online WHERE ID='".$_SESSION['ID']."' Limit 1;";
$result=mysql_query($sql) or die("Databas Error.");
$row=mysql_fetch_assoc($result);
if(empty($row))
{
...do logout stuff here
}

I think that is pretty much it, if you wanted to make a function to print out who is online then it is really easy. Just make a sql statement to select every record in the online table, except the server, and print out the results. Here is an example of such:

function showOnlineUsers()
{
$sql="SELECT ID FROM online WHERE ID!='Server';";
$result=mysql_query($sql) or die("Database Error.");
while($row=mysql_fetch_assoc($result))
{
$sql="SELECT username FROM users WHERE ID='".$row['ID']."' LIMIT 1;";
$user=mysql_query($sql) or die("Database Error.");
$user=mysql_fetch_assoc($user);
echo $user['username'];
}
}

If any one notices any mistakes including my spelling, which I'm sure their is plenty of, then just let me know so I can fix it. Hope I did a half ok job of explaining this.:pp

(The files I attached the first time are lost, if an one has them, please post them.)

Posted by Eion:
Thank you so much for this tutorial Kendall, it completely helped me get my Online players thing working for my game, you rock man and I want you to know I for one am extremely appreciative for all your help. :)


Posted by kendall14:
There is one downside to this method, not really bad though. Say you have three users online at the same time. They all say night to each other then they leave all at about the same time. No one logs into the site for say 50 minutes, during this time there will appear to be three users still logged in within the past 15 minutes. This really isn't a error because I designed the system to save bandwidth and not run the cleaner process if no one is logged. It would fix as soon as someone logged in but I just felt I should tell you this.:D


Posted by Eion:
Yeah I actually noticed that myself when I was reading through the code, but you know that bandwidth means more to me than complete accuracy so I applaud this method and am grateful for it


Posted by quantumstate:
Originally Posted by kendall14:
There is one downside to this method, not really bad though. Say you have three users online at the same time. They all say night to each other then they leave all at about the same time. No one logs into the site for say 50 minutes, during this time there will appear to be three users still logged in within the past 15 minutes. This really isn't a error because I designed the system to save bandwidth and not run the cleaner process if no one is logged. It would fix as soon as someone logged in but I just felt I should tell you this.
I am a little confused about this. You say that you did this to save bandwidth however bandwidth is the amount of information sent by the server. Updating the online users is all background processing so surely bandwidth will not be affected. The script processing time might be affected but if it was well coded I would not think that it would make much difference.


Posted by Eion:
I *believe* but am not certain, that he is saving bandwidth by coding it so that you dont have to use cronjobs, which runs even when no one is online, whereas this code runs a clean and update only when someone is online and running the script by being online, thus saving bandwidth when no one is online... man that doesnt seem like it reads right, but it made sense in my head.. its probably best to let Kendall explain :pp


Posted by kendall14
Well like he said, a cron job would come up and redo the table even when it's not needed. As for just tacking every page with it, it would be unnessiary (sp) since only people who are using the system really need to know. Also if he say found a good host but they didn't have mysql, he could go out and just get a host for mysql (which some are pretty limiting) and everything should be cool. I've had to do that last part before because I was using my HM hosting at the time and I needed to do site for a class project. (Though if you want it to redo in each page or a cron job, you could change that in a snap!)

Quantumstate
05-22-2008, 10:05 AM
It looks to me like somebody could inject some mysql with the email address. Is that what you were thinking?

kendall
05-22-2008, 01:08 PM
yeap, you got it right.

patingsadagat
06-27-2008, 12:40 PM
Thanks for this tutorial, how about tutorial for sql injection?

Jozeh
06-28-2008, 02:18 AM
sorry but is'nt people being able to inject sql into your website fields a bad thing ?

However i would like to see a sql injection tutorial, so i could... err.. safe proof my database? :P