Captive Portal Manual Logout



  • Hi guys,

    Since pop-up logout is so troublesome for end user. I tried creating a manual logout but i need badly your help.

    Objective: Using pfsense 1.2.3-Release
     1. Allow user to logout especially if its using a billed radius account (prepaid through radacct).
     2. End user do not need to allow pop-up to their browser, since mostly not really that technical.
        a. It is easier to instruct user to point their browser to "http://192.168.1.1:8000/logout", instead of step by step how to allow pop-up.
        b. If logout/disconnect is enable, it will show pop-up warning, but its too late to allow pop-up when redirection takes over.
     3. Put another checkbox at captiveportal menu.
        [] Enable logout popup window
        [] Enable Manual Logout <– though still untouched yet. help help help.

    Attempt:

    Edit system.inc and add new entry to url.rewrite.

    diff system-orig.inc system.inc

    707c707
    <               $captive_portal_rewrite = "url.rewrite-once = ( "(.captiveportal.)" => "$1", "(.*)" => "/index.php?redirurl=$1" )\n";
    –-

    $captive_portal_rewrite = "url.rewrite-once = ( "^/logout$" => "/logout.php", "(.captiveportal.)" => "$1", "(.*)" => "/index.php?redirurl=$1" )\n";
    1285c1285
    < ?>
    –-
    ?>
    \ No newline at end of file

    create logout.php at /usr/local/captiveportal

    • Code is taken from index.php but omitting almost everything but for disconnecting client.
    • The original script search for session ID from /var/db/captivportal.db and clear the found line with session id.
        So since this is a manual logout we will try to search the client ip-address instead of session id.
      –-- code start -------

    require_once("functions.inc");

    header("Expires: 0");
    header("Cache-Control: no-store, no-cache, must-revalidate");
    header("Cache-Control: post-check=0, pre-check=0", false);
    header("Pragma: no-cache");

    $clientip = $_SERVER['REMOTE_ADDR'];

    if (!$clientip) {
       /* not good - bail out */
       echo "An error occured.  Please check the system logs for more information.";
       log_error("Captive portal could not deterimine clients ip address.");
       exit;
    }

    disconnect_client($clientip,"LOGOUT",1);

    /*#############
    #From an existing captive portal-error.html this is my attempt to change $PORTAL_MESSAGE$ with my little response $logout
    ###############

    $filename= "{$g['varetc_path']}/captiveportal-error.html";
    $fd = fopen($filename, "r");
    $contents = fread ($fd, filesize($filename));
    $logout = "Thank you for using Free Internet

    You are now disconnected!";
    $fin_content = str_replace('$PORTAL_MESSAGE$', $logoutmessage, $contents);
    echo $fin_content;
    ############### */

    function disconnect_client($clientip, $logoutReason = "LOGOUT", $term_cause = 1) {

    global $g, $config;

    $cplock = lock('captiveportal');

    /* read database */
       $cpdb = captiveportal_read_db();

    $radiusservers = captiveportal_get_radius_servers();
       $found = 0;
       /* find entry */
       for ($i = 0; $i < count($cpdb); $i++) {
          if ($cpdb[$i][[b]2] == $clientip) {
               captiveportal_disconnect($cpdb[$i],$radiusservers, $term_cause);
               captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],$logoutReason);
               unset($cpdb[$i]);
                $found = 1;
               break;
           }
       }

    /* write database */
       captiveportal_write_db($cpdb);
       unlock($cplock);

    }

    ?>
    –-- code end ---------

    Disconnection is successful but a little problem there, I cannot get to throw at least a little nice response to the client's browser at least saying "You are now disconnected!"

    Thanks in advance.



  • 3. Put another checkbox at captiveportal menu.
        [] Enable logout popup window

    I've added the new option enabling disabling manual logout. I'm not sure if its the correct way, but seems to work what i want.

    # diff system-orig.inc system.inc
    707c707,711
    <              $captive_portal_rewrite = "url.rewrite-once = ( "(.captiveportal.)" => "$1", "(.*)" => "/index.php?redirurl=$1" )\n";
    –-

    if (isset($config['captiveportal']['manuallogout_enable'])) {
                          $captive_portal_rewrite = "url.rewrite-once = ( "^/logout$" => "/logout.php", "(.captiveportal.)" => "$1", "(.)" => "/index.php?redirurl=$1" )\n";
                    } else {
                          $captive_portal_rewrite = "url.rewrite-once = ( "(.captiveportal.)" => "$1", "(.
    )" =>"/index.php?redirurl=$1" )\n";
                    }

    # diff services_captiveportal-orig.php services_captiveportal.php
    64a65

    $pconfig['manuallogout_enable'] = isset($config['captiveportal']['manuallogout_enable']);
    168a170
                  $config['captiveportal']['manuallogout_enable'] = $_POST['manuallogout_enable'] ? true : false;
    234a237
          document.iform.manuallogout_enable.disabled = endis;
    327a331,339
           
              Manual Logout
             
                    >
                    Enable manual logout

    If enabled, connected client can disconnect manually through http://captiveportal-address:8000/logout. This allows clients to explicitly disconnect themselves before the idle or h
    ard timeout occurs.



  • function disconnect_client($clientip, $logoutReason = "LOGOUT", $term_cause = 1) {

    global $g, $config;

    $cplock = lock('captiveportal');

    /* read database */
        $cpdb = captiveportal_read_db();

    $radiusservers = captiveportal_get_radius_servers();
        $found = 0;
        /* find entry */
        for ($i = 0; $i < count($cpdb); $i++) {
          if ($cpdb[$i][2] == $clientip) {

    /We have to print here before captiveportal_disconnect/
                print "Thank you! You are now disconnected!";
                /*End printing to client */

    captiveportal_disconnect($cpdb[$i],$radiusservers, $term_cause);
                captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],$logoutReason);
                unset($cpdb[$i]);
                $found = 1;
                break;
            }
        }

    /* write database */
        captiveportal_write_db($cpdb);
        unlock($cplock);

    }

    This one solves my problem of disconnecting the client, also printing to client any logout message.

    New problem found though, I have to close and open a new browser for me to see the captive-portal login page because its like a stalled session or i dont know. Is there a tip how to overcome this?

    Thanks!



  • An alternate method I've tried is a php file that outputs javascript.  The javascript creates a button that when clicked recreates the logout popup, but without a redirect, avoiding the issue of not getting the popup notification.  I have added the file through the interface for adding files to the captive portal and I have my custom login page loading the javascript where I want the button to appear.  I chose this method because I didn't want to create my own logout page or logout code.

    This was mainly intended for my own use, so it may need some customization.  Also, I haven't tested it with radius and it has only been tested on 2.0 beta snapshots.  This code is based on the php code for the captive portal login in a 2.0 snapshot.

    
    require_once("auth.inc");
    require_once("functions.inc");
    require_once("captiveportal.inc");
    
    header("Expires: 0");
    header("Cache-Control: no-store, no-cache, must-revalidate");
    header("Cache-Control: post-check=0, pre-check=0", false);
    header("Pragma: no-cache");
    
    $orig_host = $_ENV['HTTP_HOST'];
    $clientip = $_SERVER['REMOTE_ADDR'];
    
    if (!$clientip) {
        /* not good - bail out */
        exit;
    }
    
    if(!isset($config['captiveportal']['logoutwin_enable'])) {
        exit;
    }
    
    if (isset($config['captiveportal']['httpslogin']))
        $ourhostname = $config['captiveportal']['httpsname'] . ":8001";
    else {
        $ifip = portal_ip_from_client_ip($clientip);
        if (!$ifip)
        	$ourhostname = $config['system']['hostname'] . ":8000";
        else
        	$ourhostname = "{$ifip}:8000";
    }
    
    $macfilter = !isset($config['captiveportal']['nomacfilter']);
    
    /* find MAC address for client */
    $clientmac = arp_get_mac_by_ip($clientip);
    if (!$clientmac && $macfilter) {
        /* unable to find MAC address - shouldn't happen! - bail out */
        exit;
    }
    
        if (isset($config['captiveportal']['httpslogin']))
            $logouturl = "https://{$config['captiveportal']['httpsname']}:8001/";
        else {
            $ifip = portal_ip_from_client_ip($clientip);
            if (!$ifip)
                $ourhostname = $config['system']['hostname'] . ":8000";
            else
                $ourhostname = "{$ifip}:8000";
            $logouturl = "http://{$ourhostname}/";
        }
    
        $cplock = lock('captiveportal');
        /* read database */
        $cpdb = captiveportal_read_db();
    
        unlock($cplock);
    
        $clientloggedin = false;
    
        /* find entry */
        for ($i = 0; $i < count($cpdb); $i++) {
            if (($macfilter && $clientmac == $cpdb[$i][3]) || $clientip == $cpdb[$i][2]) {
                $username = $cpdb[$i][4];
                $sessionid = $cpdb[$i][5];
    
    echo <<<eod<br>document.write('{$username} is currently logged in. ');
    
    function Logout_Popup()
    {
    
    LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
    if (LogoutWin) {
        LogoutWin.document.write('');
        LogoutWin.document.write('<title>Logout</title>') ;
        LogoutWin.document.write('');
        LogoutWin.document.write('') ;
        LogoutWin.document.write('**Click the button below to disconnect**
    
    ');
        LogoutWin.document.write('
    
    <form method="POST" action="{$logouturl}">');
        LogoutWin.document.write('');
        LogoutWin.document.write('');
        LogoutWin.document.write('</form>
    
    ');
        LogoutWin.document.write('');
        LogoutWin.document.write('');
        LogoutWin.document.close();
    }
    
    }
    EOD;
    
                $clientloggedin = true;
                break;
            }
        }
    
        if ($clientloggedin == false) {
    echo <<<eod<br>document.write('Login is required to access this connection.');
    EOD;
        }
    
        exit;
    
    ?></eod<br></eod<br> 
    


  • Thanks for sharing your alternatives.

    What i wanted to attain though is to give them the manual log-out that there's no need of user interface at all, just to point the browser for logout.
    Otherwise, it can cause a javascript issue as per browser dependent coding or browser version and javascript version. Though hopefully not.

    Your alternative is very much appreciated, i will look at it thoroughly if it will solve my goal.

    Thanks



  • I used javascript because I wanted to include it on the login page without having to modify the login php script.  However, it would be simple to modify the sample I posted to make it into a separate page instead of some javascript to include and also to use the logout button directly on the page instead of using a popup.  I didn't need to make it use a popup, but I just decided that I wanted it to show the original popup when clicking the button.

    Other than that I used javascript to place it on the login page, the other thing I did was to make it use the existing logout mechanism so it would be easier for me to maintain, possibly not having to change it if some of the code for logging out changes (but there is still a chance something could change that would affect it).  Of course, this also means you might not have any control over what shows up after clicking the logout button.  At least not without some javascript or modifying the captive portal php script, anyway.  I had previously tried out doing the logout by using the logout code directly, but then thought of this way of doing it so that it would not need to actually write to any files.



  • Thanks for your postings. My own problem is that disconnecting through logout pop or any other means does not stop clients time from running out. what have i done wrong? for example, if I give a client a 1hr voucher and he wants to stop browsing after 10minutes, there is no way to stop the time so that he can come back the next day to finish his time. Any solution please?



  • @estatecafe

    We are discussing 1.2.3-Release here at the moment.

    And in terms of user accounts, i am using FreeRADIUS + phpMyPrepaid and point my CP radius settings to my RADIUS.

    Since Voucher dont exist on 1.2.3-Release.  Maybe you can closely coordinate with 2.0 Developers for CP.

    Regards,


Locked