Skip to content

Commit 8ba454a

Browse files
committed
Services: Captive Portal - code cleanup in session handling and presentation.
1 parent 00e3d6d commit 8ba454a

File tree

9 files changed

+164
-278
lines changed

9 files changed

+164
-278
lines changed

src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/AccessController.php

+3-10
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,7 @@ class AccessController extends ApiControllerBase
4848
private function clientSession(string $zoneid)
4949
{
5050
$backend = new Backend();
51-
$allClientsRaw = $backend->configdpRun(
52-
"captiveportal list_clients",
53-
[$zoneid, 'json']
54-
);
51+
$allClientsRaw = $backend->configdpRun("captiveportal list_clients", [$zoneid]);
5552
$allClients = json_decode($allClientsRaw, true);
5653
if ($allClients != null) {
5754
// search for client by ip address
@@ -190,8 +187,7 @@ public function logonAction($zoneid = 0)
190187
(string)$cpZone->zoneid,
191188
$userName,
192189
$clientIp,
193-
$authServerName,
194-
'json'
190+
$authServerName
195191
]
196192
);
197193
$CPsession = json_decode($CPsession, true);
@@ -248,10 +244,7 @@ public function logoffAction($zoneid = 0)
248244
) {
249245
// you can only disconnect a connected client
250246
$backend = new Backend();
251-
$statusRAW = $backend->configdpRun(
252-
"captiveportal disconnect",
253-
[$zoneid, $clientSession['sessionId'], 'json']
254-
);
247+
$statusRAW = $backend->configdpRun("captiveportal disconnect", [$zoneid, $clientSession['sessionId']]);
255248
$status = json_decode($statusRAW, true);
256249
if ($status != null) {
257250
$this->getLogger("captiveportal")->info(

src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/SessionController.php

+32-24
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
/**
4-
* Copyright (C) 2015 Deciso B.V.
4+
* Copyright (C) 2015-2024 Deciso B.V.
55
*
66
* All rights reserved.
77
*
@@ -50,27 +50,37 @@ public function listAction($zoneid = 0)
5050
$mdlCP = new CaptivePortal();
5151
$cpZone = $mdlCP->getByZoneID($zoneid);
5252
if ($cpZone != null) {
53-
$backend = new Backend();
54-
$allClientsRaw = $backend->configdpRun(
55-
"captiveportal list_clients",
56-
array($cpZone->zoneid, 'json')
57-
);
58-
$allClients = json_decode($allClientsRaw ?? '', true);
59-
60-
return $allClients;
53+
$allClientsRaw = (new Backend())->configdpRun("captiveportal list_clients", [$cpZone->zoneid]);
54+
return json_decode($allClientsRaw ?? '', true);
6155
} else {
6256
// illegal zone, return empty response
63-
return array();
57+
return [];
6458
}
6559
}
6660

61+
/**
62+
* search through connected clients
63+
*/
64+
public function searchAction()
65+
{
66+
$this->sessionClose();
67+
$selected_zones = $this->request->get('selected_zones');
68+
$records = json_decode((new Backend())->configdRun("captiveportal list_clients") ?? '', true);
69+
70+
$response = $this->searchRecordsetBase($records, null, 'userName', function ($key) use ($selected_zones) {
71+
return empty($selected_zones) || in_array($key['zoneid'], $selected_zones);
72+
});
73+
74+
return $response;
75+
}
76+
6777
/**
6878
* return list of available zones
6979
* @return array available zones
7080
*/
7181
public function zonesAction()
7282
{
73-
$response = array();
83+
$response = [];
7484
$mdlCP = new CaptivePortal();
7585
foreach ($mdlCP->zones->zone->iterateItems() as $zone) {
7686
$response[(string)$zone->zoneid] = (string)$zone->description;
@@ -81,25 +91,24 @@ public function zonesAction()
8191

8292
/**
8393
* disconnect a client
84-
* @param string|int $zoneid zoneid
94+
* @param string|int $zoneid zoneid (deprecated)
8595
* @return array|mixed
8696
*/
87-
public function disconnectAction($zoneid = 0)
97+
public function disconnectAction($zoneid = '')
8898
{
8999
if ($this->request->isPost() && $this->request->hasPost('sessionId')) {
90-
$backend = new Backend();
91-
$statusRAW = $backend->configdpRun(
100+
$statusRAW = (new Backend())->configdpRun(
92101
"captiveportal disconnect",
93-
array($zoneid, $this->request->getPost('sessionId'), 'json')
102+
[$this->request->getPost('sessionId')]
94103
);
95-
$status = json_decode($statusRAW, true);
104+
$status = json_decode($statusRAW ?? '', true);
96105
if ($status != null) {
97106
return $status;
98107
} else {
99-
return array("status" => "Illegal response");
108+
return ["status" => "Illegal response"];
100109
}
101110
}
102-
return array();
111+
return [];
103112
}
104113

105114
/**
@@ -109,7 +118,7 @@ public function disconnectAction($zoneid = 0)
109118
*/
110119
public function connectAction($zoneid = 0)
111120
{
112-
$response = array();
121+
$response = [];
113122

114123
if ($this->request->isPost()) {
115124
// Get details from POST request
@@ -136,13 +145,12 @@ public function connectAction($zoneid = 0)
136145
$backend = new Backend();
137146
$CPsession = $backend->configdpRun(
138147
"captiveportal allow",
139-
array(
148+
[
140149
(string)$cpZone->zoneid,
141150
$userName,
142151
$clientIp,
143-
'API',
144-
'json'
145-
)
152+
'API'
153+
]
146154
);
147155

148156
// Only return session if configd returned a valid json response
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{#
22

3-
OPNsense® is Copyright © 20142015 by Deciso B.V.
3+
OPNsense® is Copyright © 20142024 by Deciso B.V.
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without modification,
@@ -30,115 +30,75 @@ POSSIBILITY OF SUCH DAMAGE.
3030
<script>
3131

3232
$( document ).ready(function() {
33-
/**
34-
* update zone list
35-
*/
36-
function updateZones() {
37-
ajaxGet("/api/captiveportal/session/zones/", {}, function(data, status) {
38-
if (status == "success") {
39-
$('#cp-zones').html("");
40-
$.each(data, function(key, value) {
41-
$('#cp-zones').append($("<option></option>").attr("value", key).text(value));
42-
});
43-
$('.selectpicker').selectpicker('refresh');
44-
// link on change event
45-
$('#cp-zones').on('change', function(){
46-
loadSessions();
47-
});
48-
// initial load sessions
49-
loadSessions();
50-
}
51-
});
52-
}
53-
54-
/**
55-
* load sessions for selected zone, hook events
56-
*/
57-
function loadSessions() {
58-
var zoneid = $('#cp-zones').find("option:selected").val();
59-
var gridopt = {
60-
ajax: false,
61-
selection: true,
62-
multiSelect: true,
63-
formatters: {
64-
"commands": function (column, row) {
65-
return '<button type="button" class="btn btn-xs btn-default command-disconnect bootgrid-tooltip" title="{{ lang._('Disconnect') }}" data-row-id="' + row.sessionId + '"><span class="fa fa-trash-o fa-fw"></span></button>';
66-
}
67-
}
68-
};
69-
if ($("#grid-clients").hasClass('bootgrid-table')) {
70-
$("#grid-clients").bootgrid('clear');
71-
} else {
72-
let grid_clients = $("#grid-clients").bootgrid(gridopt).on("loaded.rs.jquery.bootgrid", function(){
73-
// hook disconnect button
74-
grid_clients.find(".command-disconnect").on("click", function(e) {
75-
var zoneid = $('#cp-zones').find("option:selected").val();
76-
var sessionId=$(this).data("row-id");
77-
stdDialogConfirm('{{ lang._('Confirm disconnect') }}',
78-
'{{ lang._('Do you want to disconnect the selected client?') }}',
79-
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function () {
80-
ajaxCall("/api/captiveportal/session/disconnect/" + zoneid + '/',
81-
{'sessionId': sessionId}, function(data,status){
82-
// reload grid after delete
83-
loadSessions();
84-
});
85-
});
86-
});
87-
$(this).find(".bootgrid-tooltip").each(function (index) {
88-
$(this).tooltip();
89-
});
33+
ajaxGet("/api/captiveportal/session/zones/", {}, function(data, status) {
34+
if (status == "success") {
35+
$('#zone-selection').empty();
36+
$.each(data, function(key, value) {
37+
$('#zone-selection').append($("<option></option>").attr("value", key).text(value));
9038
});
39+
$('.selectpicker').selectpicker('refresh');
9140
}
92-
ajaxGet("/api/captiveportal/session/list/"+zoneid+"/", {}, function(data, status) {
93-
if (status == "success") {
94-
// format records (our bootgrid doesn't like null and expects moment for datetime)
95-
let table = [];
96-
for (var i = 0; i < data.length; i++) {
97-
let record = {};
98-
$.each(data[i], function(key, value) {
99-
record[key] = value !== null ? value : "";
41+
});
42+
43+
$("#zone-selection").on("changed.bs.select", function (e) {
44+
$("#grid-clients").bootgrid('reload');
45+
});
46+
$("#grid-clients").UIBootgrid({
47+
search:'/api/captiveportal/session/search/',
48+
datakey: 'sessionId',
49+
commands: {
50+
disconnect: {
51+
title: "{{ lang._('Disconnect') }}",
52+
method: function() {
53+
let sessid = $(this).data("row-id") !== undefined ? $(this).data("row-id") : '';
54+
stdDialogConfirm(
55+
"{{ lang._('Confirm disconnect') }}",
56+
"{{ lang._('Do you want to disconnect the selected client?') }}",
57+
"{{ lang._('Yes') }}",
58+
"{{ lang._('Cancel') }}",
59+
function () {
60+
ajaxCall("/api/captiveportal/session/disconnect",{'sessionId': sessid}, function(data,status){
61+
$("#grid-clients").bootgrid('reload');
62+
});
10063
});
101-
table.push(record);
102-
}
103-
$("#grid-clients").bootgrid('append', table);
104-
// hide actionBar on mobile
105-
$('.actionBar').addClass('hidden-xs hidden-sm');
64+
},
65+
classname: 'fa fa-trash-o fa-fw',
66+
sequence: 1,
67+
}
68+
},
69+
options: {
70+
selection: false,
71+
multiSelect: false,
72+
useRequestHandlerOnGet: true,
73+
requestHandler: function(request) {
74+
request['selected_zones'] = $("#zone-selection").val();
75+
return request;
10676
}
107-
});
108-
}
77+
}
78+
});
10979

110-
// init with first selected zone
111-
updateZones();
80+
$("#zone-selection-wrapper").detach().prependTo('#grid-clients-header > .row > .actionBar > .actions');
11281
});
11382
</script>
11483

115-
<div class="content-box">
116-
<div class="content-box-main">
117-
<div class="table-responsive">
118-
<div class="col-sm-12">
119-
<div class="pull-right">
120-
<select id="cp-zones" class="selectpicker" data-width="200px"></select>
121-
<hr/>
122-
</div>
123-
</div>
124-
<div>
125-
<table id="grid-clients" class="table table-condensed table-hover table-striped table-responsive">
126-
<thead>
127-
<tr>
128-
<th data-column-id="sessionId" data-type="string" data-identifier="true" data-visible="false">{{ lang._('Session') }}</th>
129-
<th data-column-id="userName" data-type="string">{{ lang._('Username') }}</th>
130-
<th data-column-id="macAddress" data-type="string" data-css-class="hidden-xs hidden-sm" data-header-css-class="hidden-xs hidden-sm">{{ lang._('MAC address') }}</th>
131-
<th data-column-id="ipAddress" data-type="string" data-css-class="hidden-xs hidden-sm" data-header-css-class="hidden-xs hidden-sm">{{ lang._('IP address') }}</th>
132-
<th data-column-id="startTime" data-type="datetime">{{ lang._('Connected since') }}</th>
133-
<th data-column-id="commands" data-searchable="false" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
134-
</tr>
135-
</thead>
136-
<tbody>
137-
</tbody>
138-
<tfoot>
139-
</tfoot>
140-
</table>
141-
</div>
142-
</div>
84+
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs"></ul>
85+
<div class="tab-content content-box col-xs-12 __mb">
86+
<div class="btn-group" id="zone-selection-wrapper">
87+
<select class="selectpicker" multiple="multiple" data-live-search="true" id="zone-selection" data-width="auto" title="{{ lang._('All Zones') }}">
88+
</select>
14389
</div>
90+
<table id="grid-clients" class="table table-condensed table-hover table-striped table-responsive">
91+
<thead>
92+
<tr>
93+
<th data-column-id="sessionId" data-type="string" data-identifier="true" data-visible="false">{{ lang._('Session') }}</th>
94+
<th data-column-id="userName" data-type="string">{{ lang._('Username') }}</th>
95+
<th data-column-id="macAddress" data-type="string" data-css-class="hidden-xs hidden-sm" data-header-css-class="hidden-xs hidden-sm">{{ lang._('MAC address') }}</th>
96+
<th data-column-id="ipAddress" data-type="string" data-css-class="hidden-xs hidden-sm" data-header-css-class="hidden-xs hidden-sm">{{ lang._('IP address') }}</th>
97+
<th data-column-id="startTime" data-type="datetime">{{ lang._('Connected since') }}</th>
98+
<th data-column-id="commands" data-searchable="false" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
99+
</tr>
100+
</thead>
101+
<tbody>
102+
</tbody>
103+
</table>
144104
</div>

0 commit comments

Comments
 (0)