Stabilize discovery workflow
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domovoy\Controllers;
|
||||
|
||||
use Domovoy\Repositories\DiscoveredHostRepository;
|
||||
use Domovoy\Repositories\NetworkRangeRepository;
|
||||
use Domovoy\Services\Inventory\DeviceService;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class DiscoveriesController
|
||||
{
|
||||
public function __construct(
|
||||
private DiscoveredHostRepository $discoveredHostRepository,
|
||||
private NetworkRangeRepository $networkRangeRepository,
|
||||
private DeviceService $deviceService
|
||||
) {
|
||||
}
|
||||
|
||||
public function index(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
$ranges = $this->networkRangeRepository->findAll();
|
||||
$findings = $this->discoveredHostRepository->findFiltered(
|
||||
$filters['range_id'],
|
||||
$filters['scan_job_id'],
|
||||
$filters['status'],
|
||||
200,
|
||||
$filters['sort'],
|
||||
$filters['dir']
|
||||
);
|
||||
|
||||
ob_start();
|
||||
$username = $_SESSION['username'] ?? 'User';
|
||||
require dirname(__DIR__, 2) . '/templates/discoveries/index.php';
|
||||
$body = ob_get_clean();
|
||||
$response->getBody()->write($body);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function table(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
$findings = $this->discoveredHostRepository->findFiltered(
|
||||
$filters['range_id'],
|
||||
$filters['scan_job_id'],
|
||||
$filters['status'],
|
||||
200,
|
||||
$filters['sort'],
|
||||
$filters['dir']
|
||||
);
|
||||
|
||||
ob_start();
|
||||
require dirname(__DIR__, 2) . '/templates/discoveries/_table.php';
|
||||
$body = ob_get_clean();
|
||||
$response->getBody()->write($body);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function merge(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
$hostId = isset($data['host_id']) ? (int)$data['host_id'] : 0;
|
||||
$deviceId = isset($data['device_id']) ? (int)$data['device_id'] : 0;
|
||||
|
||||
if ($hostId > 0 && $deviceId > 0) {
|
||||
$this->deviceService->mergeDiscoveredHost($hostId, $deviceId);
|
||||
}
|
||||
|
||||
$referer = $request->getHeaderLine('Referer') ?: '/discoveries';
|
||||
return $response
|
||||
->withHeader('Location', $referer)
|
||||
->withStatus(302);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{range_id: ?int, scan_job_id: ?int, status: ?string, sort: string, dir: string}
|
||||
*/
|
||||
private function filters(ServerRequestInterface $request): array
|
||||
{
|
||||
$query = $request->getQueryParams();
|
||||
$status = (string)($query['status'] ?? 'new');
|
||||
$sort = (string)($query['sort'] ?? 'ip');
|
||||
$dir = strtolower((string)($query['dir'] ?? 'asc')) === 'desc' ? 'desc' : 'asc';
|
||||
|
||||
return [
|
||||
'range_id' => isset($query['range_id']) && $query['range_id'] !== '' ? (int)$query['range_id'] : null,
|
||||
'scan_job_id' => isset($query['scan_job_id']) && $query['scan_job_id'] !== '' ? (int)$query['scan_job_id'] : null,
|
||||
'status' => $status === 'all' ? null : $status,
|
||||
'sort' => $sort,
|
||||
'dir' => $dir,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,8 @@ class DiscoveryController
|
||||
ob_start();
|
||||
$username = $_SESSION['username'] ?? 'User';
|
||||
$ranges = $this->networkRangeRepository->findAll();
|
||||
$activeJobs = $this->scanJobRepository->findActive();
|
||||
$scanJobs = $this->scanJobRepository->findRecent(10);
|
||||
$newHosts = $this->discoveredHostRepository->findByStatus('new', 20);
|
||||
require dirname(__DIR__, 2) . '/templates/discovery/index.php';
|
||||
$body = ob_get_clean();
|
||||
$response->getBody()->write($body);
|
||||
@@ -80,6 +80,53 @@ class DiscoveryController
|
||||
->withStatus(302);
|
||||
}
|
||||
|
||||
public function activeJobs(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$activeJobs = $this->scanJobRepository->findActive();
|
||||
ob_start();
|
||||
require dirname(__DIR__, 2) . '/templates/discovery/_active_jobs.php';
|
||||
$body = ob_get_clean();
|
||||
$response->getBody()->write($body);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function scanHistory(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$scanJobs = $this->scanJobRepository->findRecent(10);
|
||||
ob_start();
|
||||
require dirname(__DIR__, 2) . '/templates/discovery/_scan_history.php';
|
||||
$body = ob_get_clean();
|
||||
$response->getBody()->write($body);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function pauseJob(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
|
||||
{
|
||||
$job = $this->scanJobRepository->findById((int)$args['id']);
|
||||
if ($job !== null && $job->status === 'running') {
|
||||
$this->scanJobRepository->updateStatus($job->id, 'paused');
|
||||
}
|
||||
return $this->activeJobs($request, $response);
|
||||
}
|
||||
|
||||
public function resumeJob(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
|
||||
{
|
||||
$job = $this->scanJobRepository->findById((int)$args['id']);
|
||||
if ($job !== null && $job->status === 'paused') {
|
||||
$this->scanJobRepository->updateStatus($job->id, 'pending');
|
||||
}
|
||||
return $this->activeJobs($request, $response);
|
||||
}
|
||||
|
||||
public function cancelJob(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
|
||||
{
|
||||
$job = $this->scanJobRepository->findById((int)$args['id']);
|
||||
if ($job !== null && in_array($job->status, ['pending', 'running', 'paused'], true)) {
|
||||
$this->scanJobRepository->updateStatus($job->id, 'cancelled');
|
||||
}
|
||||
return $this->activeJobs($request, $response);
|
||||
}
|
||||
|
||||
private function createScanJob(string $type, int $rangeId): void
|
||||
{
|
||||
$job = new \Domovoy\Models\ScanJob();
|
||||
|
||||
@@ -78,17 +78,44 @@ class NetworkRangeController
|
||||
|
||||
private function isValidCidr(string $cidr): bool
|
||||
{
|
||||
if (!str_contains($cidr, '/')) {
|
||||
if (!preg_match('#^(\d+\.\d+\.\d+\.\d+)/(\d{1,2})$#', $cidr, $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
[$ip, $mask] = explode('/', $cidr, 2);
|
||||
$ip = $matches[1];
|
||||
$maskInt = (int)$matches[2];
|
||||
|
||||
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$maskInt = (int)$mask;
|
||||
return $maskInt >= 8 && $maskInt <= 32;
|
||||
if ($maskInt < 8 || $maskInt > 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$long = ip2long($ip);
|
||||
if ($long === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = (int)sprintf('%u', $long);
|
||||
return $this->isInRange($value, '10.0.0.0', 8)
|
||||
|| $this->isInRange($value, '172.16.0.0', 12)
|
||||
|| $this->isInRange($value, '192.168.0.0', 16)
|
||||
|| $this->isInRange($value, '169.254.0.0', 16)
|
||||
|| $this->isInRange($value, '127.0.0.0', 8);
|
||||
}
|
||||
|
||||
private function isInRange(int $ip, string $network, int $mask): bool
|
||||
{
|
||||
$networkLong = ip2long($network);
|
||||
if ($networkLong === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$networkValue = (int)sprintf('%u', $networkLong);
|
||||
$maskValue = (0xFFFFFFFF << (32 - $mask)) & 0xFFFFFFFF;
|
||||
|
||||
return ($ip & $maskValue) === ($networkValue & $maskValue);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user