72 lines
2.3 KiB
PHP
72 lines
2.3 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace Domovoy\Services\Ssh;
|
||
|
||
use Domovoy\Models\Credential;
|
||
use Domovoy\Services\Security\CredentialVault;
|
||
use phpseclib3\Crypt\PublicKeyLoader;
|
||
use phpseclib3\Net\SSH2;
|
||
|
||
class SshCredentialTester
|
||
{
|
||
/** @var callable(string, int, int): object */
|
||
private $clientFactory;
|
||
|
||
/**
|
||
* @param callable(string, int, int): object|null $clientFactory
|
||
* @param array<string, int> $settings
|
||
*/
|
||
public function __construct(
|
||
private CredentialVault $vault,
|
||
?callable $clientFactory = null,
|
||
private array $settings = []
|
||
) {
|
||
$this->clientFactory = $clientFactory ?? static fn (string $host, int $port, int $timeout): SSH2 => new SSH2($host, $port, $timeout);
|
||
}
|
||
|
||
/** @return array{status: string, message: string} */
|
||
public function test(Credential $credential, string $host): array
|
||
{
|
||
if ($host === '') {
|
||
return ['status' => 'failed', 'message' => 'У устройства не указан IP или hostname'];
|
||
}
|
||
|
||
try {
|
||
$client = ($this->clientFactory)(
|
||
$host,
|
||
$credential->port,
|
||
(int)($this->settings['connect_timeout'] ?? 5)
|
||
);
|
||
|
||
$auth = $this->buildAuth($credential);
|
||
$ok = $client->login($credential->username, $auth);
|
||
|
||
if ($ok) {
|
||
return ['status' => 'ok', 'message' => 'Подключение успешно'];
|
||
}
|
||
|
||
return ['status' => 'failed', 'message' => 'SSH-аутентификация не прошла'];
|
||
} catch (\Throwable $e) {
|
||
return ['status' => 'failed', 'message' => 'Ошибка SSH: ' . $e->getMessage()];
|
||
}
|
||
}
|
||
|
||
private function buildAuth(Credential $credential): mixed
|
||
{
|
||
if ($credential->authMethod === 'private_key') {
|
||
if ($credential->encryptedPrivateKey === null) {
|
||
throw new \RuntimeException('Private key is empty');
|
||
}
|
||
return PublicKeyLoader::loadPrivateKey($this->vault->decrypt($credential->encryptedPrivateKey));
|
||
}
|
||
|
||
if ($credential->encryptedSecret === null) {
|
||
throw new \RuntimeException('Password is empty');
|
||
}
|
||
|
||
return $this->vault->decrypt($credential->encryptedSecret);
|
||
}
|
||
}
|