@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
namespace App\Network\Drivers\Hsgq;
|
||||
|
||||
use App\Network\Clients\Hsgq\HsgqCliClient;
|
||||
use App\Network\Clients\Hsgq\HsgqWebClient;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class HsgqOltDriver
|
||||
{
|
||||
private const ROLES = ['RADIQ-READ' => 'user', 'RADIQ-WRITE' => 'admin', 'RADIQ-NOC' => 'admin'];
|
||||
|
||||
public function __construct(
|
||||
private HsgqCliClient $client,
|
||||
private readonly ?HsgqCliClient $writeFallback = null,
|
||||
private readonly ?HsgqWebClient $webFallback = null,
|
||||
) {}
|
||||
|
||||
public function testLogin(string $username, string $password, ?string $enablePassword = null): array
|
||||
{
|
||||
$this->client->connect($username, $password);
|
||||
try {
|
||||
$this->prepare($enablePassword ?: $password);
|
||||
|
||||
return ['users' => $this->client->command('show user')];
|
||||
} finally {
|
||||
$this->client->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public function rotateRootPassword(string $currentPassword, string $newPassword, ?string $enablePassword = null): void
|
||||
{
|
||||
$this->validatePassword($newPassword);
|
||||
try {
|
||||
$this->withWriteFallback(function () use ($currentPassword, $newPassword, $enablePassword): void {
|
||||
$this->client->connect('root', $currentPassword);
|
||||
try {
|
||||
$this->prepare($enablePassword ?: $currentPassword);
|
||||
try {
|
||||
$this->client->command('user password-self '.$newPassword);
|
||||
} catch (\RuntimeException $exception) {
|
||||
if (! str_starts_with($exception->getMessage(), 'COMMAND_REJECTED')) {
|
||||
throw $exception;
|
||||
}
|
||||
$this->client->command('user password '.$newPassword);
|
||||
}
|
||||
$this->client->command('user save');
|
||||
} finally {
|
||||
$this->client->disconnect();
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $cliException) {
|
||||
if (! $this->webFallback) {
|
||||
throw $cliException;
|
||||
}
|
||||
$this->webFallback->login('root', $currentPassword);
|
||||
$this->webFallback->changePassword('root', $newPassword, $currentPassword);
|
||||
}
|
||||
$this->testLogin('root', $newPassword, $enablePassword === $currentPassword ? $newPassword : $enablePassword);
|
||||
}
|
||||
|
||||
public function syncUser(string $rootPassword, ?string $enablePassword, string $username, string $password, string $group, bool $enabled): array
|
||||
{
|
||||
$this->validateAccount($username, $password);
|
||||
$role = self::ROLES[$group] ?? throw new InvalidArgumentException('HSGQ_ROLE_INVALID: group RADIQ tidak didukung HSGQ.');
|
||||
|
||||
try {
|
||||
return $this->withWriteFallback(function () use ($rootPassword, $enablePassword, $username, $password, $role, $enabled): array {
|
||||
$this->client->connect('root', $rootPassword);
|
||||
try {
|
||||
$this->prepare($enablePassword ?: $rootPassword);
|
||||
$users = $this->client->command('show user');
|
||||
$exists = $this->userExists($users, $username);
|
||||
if (! $enabled) {
|
||||
if ($exists) {
|
||||
$this->removeUser($username);
|
||||
}
|
||||
|
||||
return ['remote_id' => null];
|
||||
}
|
||||
|
||||
$exists
|
||||
? $this->client->command("user password {$username} {$password}")
|
||||
: $this->client->command("user add {$username} {$password} {$role} reenter 4");
|
||||
$this->client->command('user save');
|
||||
|
||||
if (! $this->userExists($this->client->command('show user'), $username)) {
|
||||
throw new \RuntimeException('COMMAND_NOT_APPLIED: user tidak ditemukan setelah sinkronisasi HSGQ.');
|
||||
}
|
||||
|
||||
return ['remote_id' => $username, 'role' => $role];
|
||||
} finally {
|
||||
$this->client->disconnect();
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $cliException) {
|
||||
return $this->syncUserViaWeb($rootPassword, $username, $password, $group, $enabled, $cliException);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteUser(string $rootPassword, ?string $enablePassword, string $username): void
|
||||
{
|
||||
$this->validateUsername($username);
|
||||
try {
|
||||
$this->withWriteFallback(function () use ($rootPassword, $enablePassword, $username): void {
|
||||
$this->client->connect('root', $rootPassword);
|
||||
try {
|
||||
$this->prepare($enablePassword ?: $rootPassword);
|
||||
if ($this->userExists($this->client->command('show user'), $username)) {
|
||||
$this->removeUser($username);
|
||||
}
|
||||
} finally {
|
||||
$this->client->disconnect();
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $cliException) {
|
||||
if (! $this->webFallback) {
|
||||
throw $cliException;
|
||||
}
|
||||
$this->webFallback->login('root', $rootPassword);
|
||||
if ($this->webFallback->hasUser($username)) {
|
||||
$this->webFallback->deleteUser($username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function removeUser(string $username): void
|
||||
{
|
||||
try {
|
||||
$this->client->command('user offline '.$username);
|
||||
} catch (\RuntimeException $exception) {
|
||||
if (! str_contains($exception->getMessage(), 'This account can not be process')) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
$this->client->command('user delete '.$username);
|
||||
$this->client->command('user save');
|
||||
}
|
||||
|
||||
private function userExists(string $users, string $username): bool
|
||||
{
|
||||
return preg_match('/^\s*'.preg_quote($username, '/').'\s+/mi', $users) === 1;
|
||||
}
|
||||
|
||||
private function withWriteFallback(Closure $operation): mixed
|
||||
{
|
||||
$primary = $this->client;
|
||||
try {
|
||||
return $operation();
|
||||
} catch (\Throwable $primaryException) {
|
||||
if (! $this->writeFallback) {
|
||||
throw $primaryException;
|
||||
}
|
||||
|
||||
$this->client = $this->writeFallback;
|
||||
try {
|
||||
return $operation();
|
||||
} catch (\Throwable $fallbackException) {
|
||||
throw new \RuntimeException('HSGQ_DUAL_CONNECTION_FAILED: SSH gagal menerapkan perubahan dan Telnet gagal: '.str($fallbackException->getMessage())->after(':')->trim()->limit(140), 0, $fallbackException);
|
||||
} finally {
|
||||
$this->client = $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function syncUserViaWeb(string $rootPassword, string $username, string $password, string $group, bool $enabled, \Throwable $cliException): array
|
||||
{
|
||||
if (! $this->webFallback) {
|
||||
throw $cliException;
|
||||
}
|
||||
|
||||
$level = match ($group) {
|
||||
'RADIQ-READ' => 5,
|
||||
'RADIQ-WRITE', 'RADIQ-NOC' => 3,
|
||||
default => throw new InvalidArgumentException('HSGQ_ROLE_INVALID: group RADIQ tidak didukung WebGUI HSGQ.'),
|
||||
};
|
||||
$this->webFallback->login('root', $rootPassword);
|
||||
$exists = $this->webFallback->hasUser($username);
|
||||
if (! $enabled) {
|
||||
if ($exists) {
|
||||
$this->webFallback->deleteUser($username);
|
||||
}
|
||||
|
||||
return ['remote_id' => null];
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
$this->webFallback->changePassword($username, $password);
|
||||
} else {
|
||||
$this->webFallback->addUser($username, $password, $level);
|
||||
}
|
||||
if (! $this->webFallback->hasUser($username)) {
|
||||
throw new \RuntimeException('COMMAND_NOT_APPLIED: user tidak ditemukan setelah sinkronisasi WebGUI HSGQ.');
|
||||
}
|
||||
|
||||
return ['remote_id' => $username, 'role' => $level, 'transport' => 'webgui'];
|
||||
}
|
||||
|
||||
private function validateAccount(string $username, string $password): void
|
||||
{
|
||||
$this->validateUsername($username);
|
||||
$this->validatePassword($password);
|
||||
}
|
||||
|
||||
private function prepare(string $enablePassword): void
|
||||
{
|
||||
$this->client->enterEnable($enablePassword);
|
||||
$this->client->command('terminal length 0');
|
||||
$this->client->command('configure');
|
||||
}
|
||||
|
||||
private function validateUsername(string $username): void
|
||||
{
|
||||
if ($username === 'root' || ! preg_match('/^[A-Za-z0-9_]{4,16}$/', $username)) {
|
||||
throw new InvalidArgumentException('HSGQ_USERNAME_INVALID: username HSGQ harus 4-16 karakter atau merupakan akun yang dilindungi.');
|
||||
}
|
||||
}
|
||||
|
||||
private function validatePassword(string $password): void
|
||||
{
|
||||
if ($password === '' || strlen($password) > 64 || preg_match('/\s/', $password)) {
|
||||
throw new InvalidArgumentException('HSGQ_PASSWORD_INVALID: password HSGQ maksimal 64 karakter tanpa spasi.');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user