Add global NAC page and refactor the per-device one (#15228)

* Adding a NAC global port page

* style

* filter_menu_entry

* typo

* ->when()

* device_id always returned

* use request

* laravel conversion, let see how it goes

* styleci

* ->with('device') to avoid 1 query per item processed

* and rewrote on @murrant advice

* any -> get

Suggested by @murrant

Co-authored-by: Tony Murray <murraytony@gmail.com>

* @push('scripts')

* style

* @endpush

---------

Co-authored-by: Tony Murray <murraytony@gmail.com>
This commit is contained in:
PipoCanaja
2023-09-01 03:25:24 +02:00
committed by GitHub
parent 298d217a8b
commit 8eeb83928f
6 changed files with 164 additions and 5 deletions

View File

@@ -0,0 +1,13 @@
<?php
namespace App\Http\Controllers;
class NacController extends Controller
{
public function index()
{
$data = [];
return view('nac', $data);
}
}

View File

@@ -34,7 +34,7 @@ class PortNacController extends TableController
public function rules()
{
return [
'device_id' => 'required|int',
'device_id' => 'int',
];
}
@@ -46,6 +46,7 @@ class PortNacController extends TableController
protected function sortFields($request)
{
return [
'device_id',
'port_id',
'mac_address',
'mac_oui',
@@ -72,10 +73,11 @@ class PortNacController extends TableController
*/
public function baseQuery($request)
{
return PortsNac::select('port_id', 'mac_address', 'ip_address', 'vlan', 'domain', 'host_mode', 'username', 'authz_by', 'timeout', 'time_elapsed', 'time_left', 'authc_status', 'authz_status', 'method')
->where('device_id', $request->device_id)
return PortsNac::select('device_id', 'port_id', 'mac_address', 'ip_address', 'vlan', 'domain', 'host_mode', 'username', 'authz_by', 'timeout', 'time_elapsed', 'time_left', 'authc_status', 'authz_status', 'method')
->when($request->device_id, fn ($q, $id) => $q->where('device_id', $id))
->hasAccess($request->user())
->with('port');
->with('port')
->with('device');
}
/**
@@ -88,6 +90,7 @@ class PortNacController extends TableController
$item['mac_oui'] = Rewrite::readableOUI($item['mac_address']);
$item['mac_address'] = Rewrite::readableMac($item['mac_address']);
$item['port'] = null; //free some unused data to be sent to the browser
$item['device_id'] = Url::deviceLink($nac->device);
return $item;
}

View File

@@ -34,6 +34,7 @@ use App\Models\Location;
use App\Models\Notification;
use App\Models\Package;
use App\Models\PortGroup;
use App\Models\PortsNac;
use App\Models\User;
use App\Models\UserPref;
use App\Models\Vminfo;
@@ -117,6 +118,8 @@ class MenuComposer
$vars['port_groups'] = PortGroup::hasAccess($user)->orderBy('name')->get(['port_groups.id', 'name', 'desc']);
$vars['port_nac'] = PortsNac::hasAccess($user)->exists();
// Sensor menu
$vars['sensor_menu'] = ObjectCache::sensors();

View File

@@ -295,6 +295,12 @@
</li>
@endif
@if($port_nac)
<li role="presentation" class="divider"></li>
<li><a href="{{ url('nac') }}"><i class="fa fa-lock fa-fw fa-lg"
aria-hidden="true"></i> NAC</a></li>
@endif
@if(auth()->user()->hasGlobalRead())
@if($port_groups_exist)
<li role="presentation" class="divider"></li>

View File

@@ -0,0 +1,132 @@
@extends('layouts.librenmsv1')
@section('title', __('NAC'))
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<x-panel title="{{ __('NAC') }}" id="nac-panel">
<div class="table-responsive">
<table id="nac-grid" data-toggle="bootgrid" class="table table-hover table-condensed table-striped">
<thead>
<tr>
<th data-column-id="device_id" data-order="asc" data-width="140px">{{ __('Devices') }}</th>
<th data-column-id="port_id" data-width="100px">{{ __('Ports') }}</th>
<th data-column-id="mac_address" data-formatter="tooltip" data-width="170px">{{ __('Mac Address') }}</th>
<th data-column-id="mac_oui" data-formatter="tooltip" data-sortable="false" data-width="130px">{{ __('Vendor') }}</th>
<th data-column-id="ip_address" data-formatter="tooltip" data-width="140px">{{ __('IP Address') }}</th>
<th data-column-id="vlan" data-formatter="tooltip" data-width="60px">{{ __('Vlan') }}</th>
<th data-column-id="domain" data-formatter="nac_domain" data-formatter="tooltip">{{ __('Domain') }}</th>
<th data-column-id="host_mode" data-formatter="nac_mode">{{ __('Host Mode') }}</th>
<th data-column-id="username" data-formatter="tooltip" data-width="250px">{{ __('Username') }}</th>
<th data-column-id="authz_by" data-formatter="tooltip">{{ __('Auth By') }}</th>
<th data-column-id="timeout" data-formatter="time_interval">{{ __('Timeout') }}</th>
<th data-column-id="time_elapsed" data-formatter="time_interval" >{{ __('Time Elapsed') }}</th>
<th data-column-id="time_left" data-formatter="time_interval" data-visible="false">{{ __('Time Left') }}</th>
<th data-column-id="authc_status" data-formatter="nac_authc" data-formatter="tooltip">{{ __('NAC Authc') }}</th>
<th data-column-id="authz_status" data-formatter="nac_authz">{{ __('NAC Authz') }}</th>
<th data-column-id="method" data-formatter="nac_method">{{ __('NAC Method') }}</th>
</tr>
</thead>
</table>
</div>
</x-panel>
</div>
</div>
</div>
@endsection
@section('css')
<style>
</style>
@endsection
@push('scripts')
<script>
$(document).ready(function () {
locations_grid = $("#nac-grid").bootgrid({
ajax: true,
rowCount: [25, 50, 100, -1],
url: "{{ route('table.port-nac') }}",
post: function () {
return {
};
},
formatters: {
"time_interval": function (column, row) {
var value = row[column.id];
var res = humanize_duration(value);
var res_light = res.split(' ').slice(0, 2).join(' ');
return "<span title=\'" + res.trim() + "\' data-toggle=\'tooltip\'>" + res_light + "</span>";
},
"tooltip": function (column, row) {
var value = row[column.id];
var vendor = '';
if (column.id == 'mac_address' && ((vendor = row['mac_oui']) != '' )) {
return "<span title=\'" + value + " (" + vendor + ")\' data-toggle=\'tooltip\'>" + value + "</span>";
}
return "<span title=\'" + value + "\' data-toggle=\'tooltip\'>" + value + "</span>";
},
"nac_authz": function (column, row) {
var value = row[column.id];
if (value === 'authorizationSuccess' || value === 'sussess') {
//typo in huawei MIB so we must keep sussess
return "<i class=\"fa fa-check-circle fa-lg icon-theme\" aria-hidden=\"true\" style=\"color:green;\"></i>";
} else if (value === 'authorizationFailed') {
return "<i class=\"fa fa-times-circle fa-lg icon-theme\" aria-hidden=\"true\" style=\"color:red;\"></i>";
} else {
return "<span class=\'label label-default\' title=\'" + value + "\' data-toggle=\'tooltip\'>" + value + "</span>";
}
},
"nac_domain": function (column, row) {
var value = row[column.id];
if (value === 'voice') {
return "<i class=\"fa fa-phone fa-lg icon-theme\" aria-hidden=\"true\"></i>";
} else if (value === 'data') {
return "<i class=\"fa fa-desktop fa-lg icon-theme\" aria-hidden=\"true\"></i>";
} else if (value === 'Disabled') {
return "<i class=\"fa fa-desktop fa-lg icon-theme\" aria-hidden=\"true\"></i>";
} else {
return "<span class=\'label label-default\' title=\'" + value + "\' data-toggle=\'tooltip\'>" + value + "</span>";
}
},
"nac_authc": function (column, row) {
var value = row[column.id];
if (value === 'notRun') {
return "<span class=\"label label-primary\">notRun</span>";
} else if (value === 'running') {
return "<span class=\"label label-primary\">running</span>";
} else if (value === 'failedOver') {
return "<i class=\"fa fa-times-circle fa-lg icon-theme\" aria-hidden=\"true\" style=\"color:red;\"></i>";
} else if (value === 'authcSuccess') {
return "<i class=\"fa fa-check-circle fa-lg icon-theme\" aria-hidden=\"true\" style=\"color:green;\">";
} else if (value === 'authcFailed') {
return "<i class=\"fa fa-times-circle fa-lg icon-theme\" aria-hidden=\"true\" style=\"color:red;\"></i>";
} else if (value === '6') {
return "<i class=\"fa fa-times-circle fa-lg icon-theme\" aria-hidden=\"true\" style=\"color:red;\"></i>";
} else {
return "<span class=\'label label-default\' title=\'" + value + "\' data-toggle=\'tooltip\'>" + value + "</span>";
}
},
"nac_method": function (column, row) {
var value = row[column.id];
if (value === 'dot1x') {
return "<span class=\"label label-success\">802.1x</span>";
} else if (value === 'macAuthBypass') {
return "<span class=\"label label-primary\">MAB</span>";
} else if (value === 'other') {
return "<span class=\"label label-danger\">Disabled</span>";
} else {
return "<span class=\'label label-default\' title=\'" + value + "\' data-toggle=\'tooltip\'>" + value + "</span>";
}
}
}
});
});
</script>
@endpush

View File

@@ -66,6 +66,8 @@ Route::middleware(['auth'])->group(function () {
Route::get('/', 'OverviewController@index')->name('home');
Route::view('vminfo', 'vminfo');
Route::get('nac', 'NacController@index');
// Device Tabs
Route::prefix('device/{device}')->namespace('Device\Tabs')->name('device.')->group(function () {
Route::put('notes', 'NotesController@update')->name('notes.update');
@@ -194,7 +196,7 @@ Route::middleware(['auth'])->group(function () {
Route::post('location', 'LocationController');
Route::post('mempools', 'MempoolsController');
Route::post('outages', 'OutagesController');
Route::post('port-nac', 'PortNacController');
Route::post('port-nac', 'PortNacController')->name('table.port-nac');
Route::post('port-stp', 'PortStpController');
Route::post('ports', 'PortsController')->name('table.ports');
Route::post('routes', 'RoutesTablesController');