diff --git a/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/AccessController.php b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/AccessController.php
index fc84fdd1079..4c7652d92ad 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/AccessController.php
+++ b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/AccessController.php
@@ -190,8 +190,7 @@ public function logonAction($zoneid = 0)
(string)$cpZone->zoneid,
$userName,
$clientIp,
- $authServerName,
- 'json'
+ $authServerName
]
);
$CPsession = json_decode($CPsession, true);
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/SessionController.php b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/SessionController.php
index 5681b3db3fc..94d85716cee 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/SessionController.php
+++ b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/SessionController.php
@@ -1,7 +1,7 @@
getByZoneID($zoneid);
if ($cpZone != null) {
- $backend = new Backend();
- $allClientsRaw = $backend->configdpRun(
- "captiveportal list_clients",
- array($cpZone->zoneid, 'json')
- );
- $allClients = json_decode($allClientsRaw ?? '', true);
-
- return $allClients;
+ $allClientsRaw = (new Backend())->configdpRun("captiveportal list_clients", [$cpZone->zoneid]);
+ return json_decode($allClientsRaw ?? '', true);
} else {
// illegal zone, return empty response
- return array();
+ return [];
}
}
+ /**
+ * search through connected clients
+ */
+ public function searchAction()
+ {
+ $this->sessionClose();
+ $selected_zones = $this->request->get('selected_zones');
+ $records = json_decode((new Backend())->configdRun("captiveportal list_clients") ?? '', true);
+
+ $response = $this->searchRecordsetBase($records, null, 'userName', function ($key) use ($selected_zones) {
+ return empty($selected_zones) || in_array($key['zoneid'], $selected_zones);
+ });
+
+ return $response;
+ }
+
/**
* return list of available zones
* @return array available zones
*/
public function zonesAction()
{
- $response = array();
+ $response = [];
$mdlCP = new CaptivePortal();
foreach ($mdlCP->zones->zone->iterateItems() as $zone) {
$response[(string)$zone->zoneid] = (string)$zone->description;
@@ -81,25 +91,24 @@ public function zonesAction()
/**
* disconnect a client
- * @param string|int $zoneid zoneid
+ * @param string|int $zoneid zoneid (deprecated)
* @return array|mixed
*/
- public function disconnectAction($zoneid = 0)
+ public function disconnectAction($zoneid = '')
{
if ($this->request->isPost() && $this->request->hasPost('sessionId')) {
- $backend = new Backend();
- $statusRAW = $backend->configdpRun(
+ $statusRAW = (new Backend())->configdpRun(
"captiveportal disconnect",
- array($zoneid, $this->request->getPost('sessionId'), 'json')
+ [$this->request->getPost('sessionId')]
);
- $status = json_decode($statusRAW, true);
+ $status = json_decode($statusRAW ?? '', true);
if ($status != null) {
return $status;
} else {
- return array("status" => "Illegal response");
+ return ["status" => "Illegal response"];
}
}
- return array();
+ return [];
}
/**
@@ -109,7 +118,7 @@ public function disconnectAction($zoneid = 0)
*/
public function connectAction($zoneid = 0)
{
- $response = array();
+ $response = [];
if ($this->request->isPost()) {
// Get details from POST request
@@ -136,13 +145,12 @@ public function connectAction($zoneid = 0)
$backend = new Backend();
$CPsession = $backend->configdpRun(
"captiveportal allow",
- array(
+ [
(string)$cpZone->zoneid,
$userName,
$clientIp,
- 'API',
- 'json'
- )
+ 'API'
+ ]
);
// Only return session if configd returned a valid json response
diff --git a/src/opnsense/mvc/app/views/OPNsense/CaptivePortal/clients.volt b/src/opnsense/mvc/app/views/OPNsense/CaptivePortal/clients.volt
index 8970b591212..7b911ccbbe7 100644
--- a/src/opnsense/mvc/app/views/OPNsense/CaptivePortal/clients.volt
+++ b/src/opnsense/mvc/app/views/OPNsense/CaptivePortal/clients.volt
@@ -1,6 +1,6 @@
{#
-OPNsense® is Copyright © 2014 – 2015 by Deciso B.V.
+OPNsense® is Copyright © 2014 – 2024 by Deciso B.V.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
@@ -30,115 +30,75 @@ POSSIBILITY OF SUCH DAMAGE.
-
-
-
-
-
-
-
-
- {{ lang._('Session') }} |
- {{ lang._('Username') }} |
- {{ lang._('MAC address') }} |
- {{ lang._('IP address') }} |
- {{ lang._('Connected since') }} |
- {{ lang._('Commands') }} |
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {{ lang._('Session') }} |
+ {{ lang._('Username') }} |
+ {{ lang._('MAC address') }} |
+ {{ lang._('IP address') }} |
+ {{ lang._('Connected since') }} |
+ {{ lang._('Commands') }} |
+
+
+
+
+
diff --git a/src/opnsense/scripts/OPNsense/CaptivePortal/allow.py b/src/opnsense/scripts/OPNsense/CaptivePortal/allow.py
index d090e01b433..937bb604752 100755
--- a/src/opnsense/scripts/OPNsense/CaptivePortal/allow.py
+++ b/src/opnsense/scripts/OPNsense/CaptivePortal/allow.py
@@ -1,7 +1,7 @@
#!/usr/local/bin/python3
"""
- Copyright (c) 2015-2019 Ad Schellevis
+ Copyright (c) 2015-2024 Ad Schellevis
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -28,48 +28,28 @@
--------------------------------------------------------------------------------------
allow user/host to captive portal
"""
+import argparse
import sys
import ujson
from lib.db import DB
from lib.arp import ARP
from lib.ipfw import IPFW
-# parse input parameters
-parameters = {'username': '', 'ip_address': None, 'zoneid': None, 'authenticated_via': None, 'output_type': 'plain'}
-current_param = None
-for param in sys.argv[1:]:
- if len(param) > 1 and param[0] == '/':
- current_param = param[1:].lower()
- elif current_param is not None:
- if current_param in parameters:
- parameters[current_param] = param.strip()
- current_param = None
-
-# create new session
-if parameters['ip_address'] is not None and parameters['zoneid'] is not None:
- cpDB = DB()
- cpIPFW = IPFW()
- arp_entry = ARP().get_by_ipaddress(parameters['ip_address'])
- if arp_entry is not None:
- mac_address = arp_entry['mac']
- else:
- mac_address = None
-
- response = cpDB.add_client(zoneid=parameters['zoneid'],
- authenticated_via=parameters['authenticated_via'],
- username=parameters['username'],
- ip_address=parameters['ip_address'],
- mac_address=mac_address
- )
- cpIPFW.add_to_table(table_number=parameters['zoneid'], address=parameters['ip_address'])
- response['clientState'] = 'AUTHORIZED'
-else:
- response = {'clientState': 'UNKNOWN'}
-
-
-# output result as plain text or json
-if parameters['output_type'] != 'json':
- for item in response:
- print ('%20s %s' % (item, response[item]))
-else:
- print(ujson.dumps(response))
+parser = argparse.ArgumentParser()
+parser.add_argument('-username', help='username', type=str, required=True)
+parser.add_argument('-zoneid', help='zone number to allow this user in', type=str, required=True)
+parser.add_argument('-authenticated_via', help='authentication source', type=str)
+parser.add_argument('-ip_address', help='source ip address', type=str)
+args = parser.parse_args()
+
+arp_entry = ARP().get_by_ipaddress(args.ip_address)
+response = DB().add_client(
+ zoneid=args.zoneid,
+ authenticated_via=args.authenticated_via,
+ username=args.username,
+ ip_address=args.ip_address,
+ mac_address=arp_entry['mac'] if arp_entry is not None else None
+)
+IPFW().add_to_table(table_number=args.zoneid, address=args.ip_address)
+response['clientState'] = 'AUTHORIZED'
+print(ujson.dumps(response))
diff --git a/src/opnsense/scripts/OPNsense/CaptivePortal/disconnect.py b/src/opnsense/scripts/OPNsense/CaptivePortal/disconnect.py
index d103a402a4c..6721d5cf85f 100755
--- a/src/opnsense/scripts/OPNsense/CaptivePortal/disconnect.py
+++ b/src/opnsense/scripts/OPNsense/CaptivePortal/disconnect.py
@@ -1,7 +1,7 @@
#!/usr/local/bin/python3
"""
- Copyright (c) 2015-2019 Ad Schellevis
+ Copyright (c) 2015-2024 Ad Schellevis
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -28,35 +28,23 @@
--------------------------------------------------------------------------------------
disconnect client
"""
-import sys
+import argparse
import ujson
from lib.db import DB
from lib.ipfw import IPFW
-# parse input parameters
-parameters = {'sessionid': None, 'zoneid': None, 'output_type': 'plain'}
-current_param = None
-for param in sys.argv[1:]:
- if len(param) > 1 and param[0] == '/' and param[1:] in parameters:
- current_param = param[1:].lower()
- elif current_param is not None:
- parameters[current_param] = param.strip()
- current_param = None
-
-# disconnect client
+
+parser = argparse.ArgumentParser()
+parser.add_argument('session', help='session id to delete', type=str)
+parser.add_argument('-z', help='optional zoneid to filter on', type=str)
+args = parser.parse_args()
+
response = {'terminateCause': 'UNKNOWN'}
-if parameters['sessionid'] is not None and parameters['zoneid'] is not None:
- # remove client
- client_session_info = DB().del_client(parameters['zoneid'], parameters['sessionid'])
- if client_session_info is not None:
- if client_session_info['ip_address']:
- IPFW().delete(parameters['zoneid'], client_session_info['ip_address'])
- client_session_info['terminateCause'] = 'User-Request'
- response = client_session_info
-
-# output result as plain text or json
-if parameters['output_type'] != 'json':
- for item in response:
- print ('%20s %s' % (item, response[item]))
-else:
- print(ujson.dumps(response))
+client_session_info = DB().del_client(int(args.z) if str(args.z).isdigit() else None, args.session)
+if client_session_info is not None:
+ if client_session_info['ip_address']:
+ IPFW().delete(client_session_info['zoneid'], client_session_info['ip_address'])
+ client_session_info['terminateCause'] = 'User-Request'
+ response = client_session_info
+
+print(ujson.dumps(response))
diff --git a/src/opnsense/scripts/OPNsense/CaptivePortal/lib/db.py b/src/opnsense/scripts/OPNsense/CaptivePortal/lib/db.py
index d309ef7e668..e9a415d530f 100755
--- a/src/opnsense/scripts/OPNsense/CaptivePortal/lib/db.py
+++ b/src/opnsense/scripts/OPNsense/CaptivePortal/lib/db.py
@@ -167,7 +167,7 @@ def del_client(self, zoneid, sessionid):
cur.execute(""" select *
from cp_clients
where sessionid = :sessionid
- and zoneid = :zoneid
+ and (zoneid = :zoneid or :zoneid is null)
and deleted = 0
""", {'zoneid': zoneid, 'sessionid': sessionid})
data = cur.fetchall()
@@ -176,15 +176,17 @@ def del_client(self, zoneid, sessionid):
for fields in cur.description:
session_info[fields[0]] = data[0][len(session_info)]
# remove client
- cur.execute("update cp_clients set deleted = 1 where sessionid = :sessionid and zoneid = :zoneid",
- {'zoneid': zoneid, 'sessionid': sessionid})
+ cur.execute(
+ "update cp_clients set deleted = 1 where sessionid = :sessionid",
+ {'zoneid': zoneid, 'sessionid': sessionid}
+ )
self._connection.commit()
return session_info
else:
return None
- def list_clients(self, zoneid):
+ def list_clients(self, zoneid=None):
""" return list of (administrative) connected clients and usage statistics
:param zoneid: zone id
:return: list of clients
@@ -212,7 +214,7 @@ def list_clients(self, zoneid):
from cp_clients cc
left join session_info si on si.zoneid = cc.zoneid and si.sessionid = cc.sessionid
left join session_restrictions sr on sr.zoneid = cc.zoneid and sr.sessionid = cc.sessionid
- where cc.zoneid = :zoneid
+ where (cc.zoneid = :zoneid or :zoneid is null)
and cc.deleted = 0
order by case when cc.username is not null then cc.username else cc.ip_address end
, cc.created desc
diff --git a/src/opnsense/scripts/OPNsense/CaptivePortal/listClients.py b/src/opnsense/scripts/OPNsense/CaptivePortal/listClients.py
index 58e2c5c0c72..2c8c7336f72 100755
--- a/src/opnsense/scripts/OPNsense/CaptivePortal/listClients.py
+++ b/src/opnsense/scripts/OPNsense/CaptivePortal/listClients.py
@@ -1,7 +1,7 @@
#!/usr/local/bin/python3
"""
- Copyright (c) 2015-2019 Ad Schellevis
+ Copyright (c) 2015-2024 Ad Schellevis
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -26,50 +26,16 @@
POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------------
- list connected clients for a captive portal zone
+ list connected clients for a (or all) captive portal zone(s)
"""
-import sys
-import time
+import argparse
import ujson
from lib.db import DB
# parse input parameters
-parameters = {'zoneid': None, 'output_type': 'plain'}
-current_param = None
-for param in sys.argv[1:]:
- if len(param) > 1 and param[0] == '/':
- current_param = param[1:].lower()
- elif current_param is not None:
- if current_param in parameters:
- parameters[current_param] = param.strip()
- current_param = None
+parser = argparse.ArgumentParser()
+parser.add_argument('-z', help='optional zoneid to filter on', type=str)
+args = parser.parse_args()
-if parameters['zoneid'] is not None:
- response = DB().list_clients(parameters['zoneid'])
-else:
- response = []
-
-# output result as plain text or json
-if parameters['output_type'] != 'json':
- heading = {
- 'sessionId': 'sessionid',
- 'userName': 'username',
- 'ipAddress': 'ip_address',
- 'macAddress': 'mac_address',
- 'total_bytes': 'total_bytes',
- 'idletime': 'idletime',
- 'totaltime': 'totaltime',
- 'acc_timeout': 'acc_session_timeout'
- }
- heading_format = '%(sessionId)-30s %(userName)-25s %(ipAddress)-20s %(macAddress)-20s '\
- + '%(total_bytes)-15s %(idletime)-10s %(totaltime)-10s %(acc_timeout)-10s'
- print (heading_format % heading)
- for item in response:
- item['total_bytes'] = (item['bytes_out'] + item['bytes_in'])
- item['idletime'] = time.time() - item['last_accessed']
- item['totaltime'] = time.time() - item['startTime']
- frmt = '%(sessionId)-30s %(userName)-25s %(ipAddress)-20s %(macAddress)-20s '\
- + '%(total_bytes)-15s %(idletime)-10d %(totaltime)-10d %(acc_session_timeout)-10s'
- print (frmt % item)
-else:
- print(ujson.dumps(response))
+response = DB().list_clients(int(args.z) if str(args.z).isdigit() else None)
+print(ujson.dumps(response))
diff --git a/src/opnsense/scripts/OPNsense/CaptivePortal/set_session_restrictions.py b/src/opnsense/scripts/OPNsense/CaptivePortal/set_session_restrictions.py
index b98f365598b..631685fc545 100755
--- a/src/opnsense/scripts/OPNsense/CaptivePortal/set_session_restrictions.py
+++ b/src/opnsense/scripts/OPNsense/CaptivePortal/set_session_restrictions.py
@@ -1,6 +1,6 @@
#!/usr/local/bin/python3
"""
- Copyright (c) 2015-2019 Ad Schellevis
+ Copyright (c) 2015-2024 Ad Schellevis
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -26,29 +26,18 @@
--------------------------------------------------------------------------------------
update (or add) client/session restrictions
"""
-import sys
+import argparse
import ujson
from lib.db import DB
-parameters = {'zoneid': '', 'sessionid': None, 'session_timeout': None, 'output_type': 'plain'}
-current_param = None
-for param in sys.argv[1:]:
- if len(param) > 1 and param[0] == '/' and param[1:] in parameters:
- current_param = param[1:].lower()
- elif current_param is not None:
- parameters[current_param] = param.strip()
- current_param = None
+parser = argparse.ArgumentParser()
+parser.add_argument('-zoneid', help='zone number to allow this user in', type=str, required=True)
+parser.add_argument('-sessionid', help='session id', type=str, required=True)
+parser.add_argument('-session_timeout', help='authentication source', type=str)
+args = parser.parse_args()
-response = dict()
-if parameters['zoneid'] is not None and parameters['sessionid'] is not None:
- db = DB()
- response['response'] = db.update_session_restrictions(parameters['zoneid'],
- parameters['sessionid'],
- parameters['session_timeout'])
-# output result as plain text or json
-if parameters['output_type'] != 'json':
- for item in response:
- print ('%20s %s' % (item, response[item]))
-else:
- print(ujson.dumps(response))
+response = {
+ 'response': DB().update_session_restrictions(args.zoneid, args.sessionid, args.session_timeout)
+}
+print(ujson.dumps(response))
diff --git a/src/opnsense/service/conf/actions.d/actions_captiveportal.conf b/src/opnsense/service/conf/actions.d/actions_captiveportal.conf
index e6282dcf583..79a2f76c9c2 100644
--- a/src/opnsense/service/conf/actions.d/actions_captiveportal.conf
+++ b/src/opnsense/service/conf/actions.d/actions_captiveportal.conf
@@ -1,24 +1,24 @@
[list_clients]
command:/usr/local/opnsense/scripts/OPNsense/CaptivePortal/listClients.py
-parameters:/zoneid %s /output_type %s
+parameters:-z %s
type:script_output
message:list registered clients
[allow]
command:/usr/local/opnsense/scripts/OPNsense/CaptivePortal/allow.py
-parameters:/zoneid %s /username %s /ip_address %s /authenticated_via %s /output_type %s
+parameters:-zoneid %s -username %s -ip_address %s -authenticated_via %s
type:script_output
message:allow client access to captive portal
[disconnect]
command:/usr/local/opnsense/scripts/OPNsense/CaptivePortal/disconnect.py
-parameters:/zoneid %s /sessionid %s /output_type %s
+parameters:%s
type:script_output
message:disconnect client
[set.session_restrictions]
command:/usr/local/opnsense/scripts/OPNsense/CaptivePortal/set_session_restrictions.py
-parameters:/zoneid %s /sessionid %s /session_timeout %s /output_type %s
+parameters:-zoneid %s -sessionid %s -session_timeout %s
type:script_output
message:set extra restrictions for session (%s) %s