Commit 3cfac591 authored by Ad Schellevis's avatar Ad Schellevis

(Captiveportal, new) send radius accounting messages and cleanup sessions

parent fe45340c
...@@ -33,6 +33,7 @@ import ujson ...@@ -33,6 +33,7 @@ import ujson
import time import time
import syslog import syslog
import traceback import traceback
import subprocess
from lib import Config from lib import Config
from lib.db import DB from lib.db import DB
from lib.arp import ARP from lib.arp import ARP
...@@ -190,6 +191,7 @@ def main(): ...@@ -190,6 +191,7 @@ def main():
""" Background process loop, runs as backend daemon for all zones. only one should be active at all times. """ Background process loop, runs as backend daemon for all zones. only one should be active at all times.
The main job of this procedure is to sync the administration with the actual situation in the ipfw firewall. The main job of this procedure is to sync the administration with the actual situation in the ipfw firewall.
""" """
last_cleanup_timestamp = 0
bgprocess = CPBackgroundProcess() bgprocess = CPBackgroundProcess()
bgprocess.initialize_fixed() bgprocess.initialize_fixed()
...@@ -198,6 +200,11 @@ def main(): ...@@ -198,6 +200,11 @@ def main():
# open database # open database
bgprocess.db.open() bgprocess.db.open()
# cleanup old settings, every 5 minutes
if time.time() - last_cleanup_timestamp > 300:
bgprocess.db.cleanup_sessions()
last_cleanup_timestamp = time.time()
# reload cached arp table contents # reload cached arp table contents
bgprocess.arp.reload() bgprocess.arp.reload()
...@@ -211,6 +218,9 @@ def main(): ...@@ -211,6 +218,9 @@ def main():
# close the database handle while waiting for the next poll # close the database handle while waiting for the next poll
bgprocess.db.close() bgprocess.db.close()
# process accounting messages (uses php script, for reuse of Auth classes)
subprocess.call(['/usr/local/opnsense/scripts/OPNsense/CaptivePortal/process_accounting_messages.php'])
# sleep # sleep
time.sleep(5) time.sleep(5)
except KeyboardInterrupt: except KeyboardInterrupt:
......
...@@ -355,3 +355,49 @@ class DB(object): ...@@ -355,3 +355,49 @@ class DB(object):
else: else:
self._connection.commit() self._connection.commit()
return 'update' return 'update'
def cleanup_sessions(self):
""" cleanup removed sessions, but wait for accounting to finish when busy
"""
cur = self._connection.cursor()
cur.execute(""" delete
from cp_clients
where cp_clients.deleted = 1
and not exists (
select 1
from accounting_state
where accounting_state.zoneid = cp_clients.zoneid
and accounting_state.sessionid = cp_clients.sessionid
and accounting_state.state <> 'STOPPED'
)
""")
cur.execute(""" delete
from accounting_state
where not exists (
select 1
from cp_clients
where cp_clients.zoneid = accounting_state.zoneid
and cp_clients.sessionid = accounting_state.sessionid
)
""")
cur.execute(""" delete
from session_info
where not exists (
select 1
from cp_clients
where session_info.zoneid = cp_clients.zoneid
and session_info.sessionid = cp_clients.sessionid
)
""")
cur.execute(""" delete
from session_restrictions
where not exists (
select 1
from cp_clients
where session_restrictions.zoneid = cp_clients.zoneid
and session_restrictions.sessionid = cp_clients.sessionid
)
""")
self._connection.commit()
#!/usr/local/bin/php
<?php
/**
* Copyright (C) 2015 Deciso B.V.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
require_once('script/load_phalcon.php');
use OPNsense\Core\Config;
use OPNsense\Auth\AuthenticationFactory;
// open database
$database_filename = '/var/captiveportal/captiveportal.sqlite';
$db = new SQLite3($database_filename);
// query all sessions with client restrictions
$result = $db->query('
select c.zoneid
, c.sessionid
, c.username
, c.authenticated_via
, c.deleted
, c.created
, accs.state
from cp_clients c
inner join session_restrictions sr on sr.zoneid = c.zoneid and sr.sessionid = c.sessionid
left join session_info si on c.zoneid = si.zoneid and c.sessionid = si.sessionid
left join accounting_state accs on accs.zoneid = c.zoneid and accs.sessionid = c.sessionid
order by c.authenticated_via
');
// process all sessions
while($row = $result->fetchArray(SQLITE3_ASSOC) ){
$authFactory = new OPNsense\Auth\AuthenticationFactory;
$authenticator = $authFactory->get($row['authenticated_via']);
if ($authenticator != null) {
if ($row['state'] == null) {
// new accounting state, send start event (if applicable)
$stmt = $db->prepare('insert into accounting_state(zoneid, sessionid, state)
values (:zoneid, :sessionid, \'RUNNING\')');
$stmt->bindParam(':zoneid', $row['zoneid']);
$stmt->bindParam(':sessionid', $row['sessionid']);
$stmt->execute();
if (method_exists($authenticator,'startAccounting')) {
// send start accounting event
$authenticator->startAccounting($row['username'], $row['sessionid']);
}
} elseif ($row['deleted'] == 1 && $row['state'] != 'STOPPED') {
// stop accounting, send stop event (if applicable)
$stmt = $db->prepare('update accounting_state
set state = \'STOPPED\'
where zoneid = :zoneid
and sessionid = :sessionid');
$stmt->bindParam(':zoneid', $row['zoneid']);
$stmt->bindParam(':sessionid', $row['sessionid']);
$stmt->execute();
if (method_exists($authenticator,'startAccounting')) {
$time_spend = time() - $row['created'];
$authenticator->stopAccounting($row['username'], $row['sessionid'], $time_spend);
}
} elseif ($row['state'] != 'STOPPED') {
// send interim updates (if applicable)
if (method_exists($authenticator,'updateAccounting')) {
// send interim update event
$time_spend = time() - $row['created'];
$authenticator->updateAccounting($row['username'], $row['sessionid'], $time_spend);
}
}
}
}
$db->close();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment