@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace App\Network\Drivers\Hisfocus;
|
||||
|
||||
use App\Network\Clients\Hsgq\HsgqCliClient;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
class HisfocusOltDriver
|
||||
{
|
||||
private const ROLES = ['RADIQ-READ' => 'guest', 'RADIQ-WRITE' => 'operator', 'RADIQ-NOC' => 'administrator'];
|
||||
|
||||
public function __construct(private readonly HsgqCliClient $client) {}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function probe(string $username, string $password, ?string $enablePassword): array
|
||||
{
|
||||
$this->client->connect($username, $password);
|
||||
try {
|
||||
$this->client->enterEnable($enablePassword ?: $password);
|
||||
$facts = ['management_transport' => str_contains($this->client::class, 'Ssh') ? 'ssh' : 'telnet', 'supported_probes' => []];
|
||||
foreach (['show version', 'show system information', 'show system'] as $command) {
|
||||
try {
|
||||
$output = $this->client->command($command);
|
||||
$facts['supported_probes'][] = $command;
|
||||
$facts += $this->parseFacts($output);
|
||||
} catch (\Throwable) {
|
||||
// Firmware Hisfocus berbeda-beda; satu command unsupported tidak menggagalkan probe lain.
|
||||
}
|
||||
}
|
||||
|
||||
return $facts;
|
||||
} finally {
|
||||
$this->client->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public function provisionBaseAccess(string $loginUsername, string $loginPassword, ?string $enablePassword, string $newUsername, string $newPassword): array
|
||||
{
|
||||
$this->validateAccount($newUsername, $newPassword);
|
||||
$this->client->connect($loginUsername, $loginPassword);
|
||||
try {
|
||||
$this->client->enterEnable($enablePassword ?: $loginPassword);
|
||||
$users = $this->client->command('show users');
|
||||
$this->client->command('configure terminal');
|
||||
$this->client->command($this->userExists($users, $newUsername)
|
||||
? "user change {$newUsername} {$newPassword}"
|
||||
: "user create administrator {$newUsername} {$newPassword}");
|
||||
$this->save();
|
||||
} finally {
|
||||
$this->client->disconnect();
|
||||
}
|
||||
$this->testLogin($newUsername, $newPassword, $enablePassword ?: $loginPassword);
|
||||
|
||||
return ['username' => $newUsername, 'role' => 'administrator'];
|
||||
}
|
||||
|
||||
public function testLogin(string $username, string $password, ?string $enablePassword): array
|
||||
{
|
||||
$this->client->connect($username, $password);
|
||||
try {
|
||||
$this->client->enterEnable($enablePassword ?: $password);
|
||||
|
||||
return ['users' => $this->client->command('show users')] + $this->probeFacts();
|
||||
} finally {
|
||||
$this->client->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public function syncUser(string $loginUsername, string $loginPassword, ?string $enablePassword, string $username, string $password, string $group, bool $enabled): array
|
||||
{
|
||||
$this->validateAccount($username, $password);
|
||||
$role = self::ROLES[$group] ?? throw new InvalidArgumentException('HISFOCUS_ROLE_INVALID: group RADIQ tidak didukung Hisfocus.');
|
||||
$this->client->connect($loginUsername, $loginPassword);
|
||||
try {
|
||||
$this->client->enterEnable($enablePassword ?: $loginPassword);
|
||||
$users = $this->client->command('show users');
|
||||
$exists = $this->userExists($users, $username);
|
||||
$this->client->command('configure terminal');
|
||||
if (! $enabled) {
|
||||
if ($exists) {
|
||||
$this->client->command('user delete '.$username);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return ['remote_id' => null];
|
||||
}
|
||||
$this->client->command($exists
|
||||
? "user change {$username} {$password}"
|
||||
: "user create {$role} {$username} {$password}");
|
||||
$this->save();
|
||||
} finally {
|
||||
$this->client->disconnect();
|
||||
}
|
||||
$result = $this->testLogin($loginUsername, $loginPassword, $enablePassword);
|
||||
if (! $this->userExists($result['users'], $username)) {
|
||||
throw new RuntimeException('COMMAND_NOT_APPLIED: user tidak ditemukan setelah sinkronisasi Hisfocus.');
|
||||
}
|
||||
|
||||
return ['remote_id' => $username, 'role' => $role];
|
||||
}
|
||||
|
||||
public function deleteUser(string $loginUsername, string $loginPassword, ?string $enablePassword, string $username): void
|
||||
{
|
||||
$this->validateUsername($username);
|
||||
$this->client->connect($loginUsername, $loginPassword);
|
||||
try {
|
||||
$this->client->enterEnable($enablePassword ?: $loginPassword);
|
||||
if (! $this->userExists($this->client->command('show users'), $username)) {
|
||||
return;
|
||||
}
|
||||
$this->client->command('configure terminal');
|
||||
$this->client->command('user delete '.$username);
|
||||
$this->save();
|
||||
} finally {
|
||||
$this->client->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private function probeFacts(): array
|
||||
{
|
||||
foreach (['show system', 'show version'] as $command) {
|
||||
try {
|
||||
return $this->parseFacts($this->client->command($command));
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function save(): void
|
||||
{
|
||||
$this->client->command('exit');
|
||||
$this->client->command('write');
|
||||
}
|
||||
|
||||
private function userExists(string $users, string $username): bool
|
||||
{
|
||||
return preg_match('/User\s+\d+\s*:\s*'.preg_quote($username, '/').'\s+/mi', $users) === 1;
|
||||
}
|
||||
|
||||
private function validateAccount(string $username, string $password): void
|
||||
{
|
||||
$this->validateUsername($username);
|
||||
if ($password === '' || strlen($password) > 64 || preg_match('/\s/', $password)) {
|
||||
throw new InvalidArgumentException('HISFOCUS_PASSWORD_INVALID: password wajib diisi, maksimal 64 karakter, dan tanpa spasi.');
|
||||
}
|
||||
}
|
||||
|
||||
private function validateUsername(string $username): void
|
||||
{
|
||||
if (! preg_match('/^[A-Za-z][A-Za-z0-9_]{3,15}$/', $username)) {
|
||||
throw new InvalidArgumentException('HISFOCUS_USERNAME_INVALID: username harus 4-16 karakter, diawali huruf, dan hanya alfanumerik/underscore.');
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
private function parseFacts(string $output): array
|
||||
{
|
||||
$patterns = [
|
||||
'model' => '/(?:product\s*model|device\s*model|\bmodel)\s*[:=]\s*([^\r\n]+)/i',
|
||||
'serial_number' => '/(?:serial\s*(?:number|no\.?|num)|\bSN)\s*[:=]\s*([^\r\n]+)/i',
|
||||
'firmware_version' => '/(?:firmware|software|system)(?:\s*version)?\s*[:=]\s*([^\r\n]+)/i',
|
||||
'hardware_version' => '/hardware(?:\s*version)?\s*[:=]\s*([^\r\n]+)/i',
|
||||
'mac_address' => '/(?:base\s*)?mac(?:\s*address)?\s*[:=]\s*([0-9a-f:-]{12,17})/i',
|
||||
];
|
||||
$facts = [];
|
||||
foreach ($patterns as $key => $pattern) {
|
||||
if (preg_match($pattern, $output, $match)) {
|
||||
$facts[$key] = trim($match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return $facts;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user