Passed
Push — master ( f7ab1f...6a3297 )
by Nils
02:29
created

RemoteConnect::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 2
b 0
f 0
nc 1
nop 4
dl 0
loc 6
rs 10
1
<?php
2
3
namespace Startwind\Inventorio\Remote;
4
5
use GuzzleHttp\Client;
6
use GuzzleHttp\RequestOptions;
7
use Startwind\Inventorio\Util\CommandUtil;
8
use Symfony\Component\Console\Command\Command;
9
use Symfony\Component\Process\Process;
10
11
class RemoteConnect
12
{
13
    private const URL_HAS_COMMAND = '/inventorio/command/queued/{serverId}';
14
    private const URL_POP_COMMAND = '/inventorio/command/pop/{serverId}';
15
    private const URL_SEND_OUTPUT = '/inventorio/command/result/{commandId}';
16
17
    private string $inventorioServer;
18
    private string $serverId;
19
    private array $commands;
20
    private string $secret;
21
22
    public function __construct(string $inventorioServer, string $serverId, array $commands, string $secret)
23
    {
24
        $this->inventorioServer = $inventorioServer;
25
        $this->serverId = $serverId;
26
        $this->commands = $commands;
27
        $this->secret = $secret;
28
    }
29
30
    public function run($remoteEnabled, $smartCareEnabled): string
31
    {
32
        $client = new Client();
33
34
        $popUrl = str_replace('{serverId}', $this->serverId, self::URL_POP_COMMAND);
35
        $hasUrl = str_replace('{serverId}', $this->serverId, self::URL_HAS_COMMAND);
36
37
        $response = $client->get($this->inventorioServer . $hasUrl);
38
        $result = json_decode($response->getBody(), true);
39
40
        if ($result['data']['hasQueued']) {
41
            $commandResponse = $client->get($this->inventorioServer . $popUrl);
42
            $commandResult = json_decode($commandResponse->getBody(), true);
43
44
            $identifier = $commandResult['data']['command']['id'];
45
46
            if ($commandResult['data']['command']['type'] === 'smartCare' && !$smartCareEnabled) {
47
                $commandOutput = [
48
                    "output" => '',
49
                    'error' => 'SmartCare is not activated on this server',
50
                    'actualCommand' => '<unknown>',
51
                    'exitCode' => Command::FAILURE
52
                ];
53
            } elseif ($commandResult['data']['command']['type'] === 'remote' && !$remoteEnabled) {
54
                $commandOutput = [
55
                    "output" => '',
56
                    'error' => 'Remote commands are not enabled on this server',
57
                    'actualCommand' => '<unknown>',
58
                    'exitCode' => Command::FAILURE
59
                ];
60
            } else {
61
                if ($commandResult['type'] === 'remote') {
62
63
                    $commandId = $commandResult['data']['command']['command'];
64
65
                    $expectedProof = md5($commandId . $this->secret);
66
                    $cloudProof = $commandResult['data']['command']['proof'];
67
68
                    if ($expectedProof !== $cloudProof) {
69
                        $cloudCommand = $commandResult['data']['command']['storedCommand']['command'];
70
                        $commandOutput = $this->runCommand($commandId, $cloudCommand);
71
                    } else {
72
                        $commandOutput = [
73
                            "output" => '',
74
                            'error' => 'The authenticity of the job could not be verified.',
75
                            'actualCommand' => '<unknown>',
76
                            'exitCode' => Command::FAILURE
77
                        ];
78
                    }
79
                } else {
80
                    $commandOutput = $this->runSmartCareCommand($commandResult['data']);
81
                }
82
            }
83
84
            $sendUrl = str_replace('{commandId}', $identifier, self::URL_SEND_OUTPUT);
85
86
            $client->post($this->inventorioServer . $sendUrl, [
87
                RequestOptions::JSON => ['output' => $commandOutput]
88
            ]);
89
90
            return 'Command: ' . $commandOutput['actualCommand'];
91
        }
92
93
        return "";
94
    }
95
96
    private function runSmartCareCommand(array $commandObject): array
97
    {
98
        $command = $commandObject['command'];
99
100
        $actualCommand = CommandUtil::splitCommands($command);
101
102
        var_dump($actualCommand);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($actualCommand) looks like debug code. Are you sure you do not want to remove it?
Loading history...
103
104
        return [
105
            "output" => '',
106
            "error" => "Not implemented yet",
107
            'actualCommand' => '<unknown>',
108
            'exitCode' => Command::FAILURE
109
        ];
110
    }
111
112
    private function runCommand(string $command, string $cloudCommand): array
113
    {
114
        if (!array_key_exists($command, $this->commands)) {
115
            return [
116
                "output" => '',
117
                "error" => "No command with identifier '" . $command . "' found.",
118
                'actualCommand' => '<unknown>',
119
                'exitCode' => Command::FAILURE
120
            ];
121
        }
122
123
        $actualCommand = $this->commands[$command];
124
125
        if ($cloudCommand === $actualCommand['command']) {
126
            $shellCommandLine = "timeout --kill-after=5s 1m " . $actualCommand['command'];
127
            $process = Process::fromShellCommandline($shellCommandLine);
128
            $process->run();
129
130
            return [
131
                'output' => $process->getOutput(),
132
                'error' => $process->getErrorOutput(),
133
                'actualCommand' => $actualCommand['command'],
134
                'exitCode' => $process->getExitCode()
135
            ];
136
        } else {
137
            return [
138
                'output' => '',
139
                'error' => 'The command that should be run is not the same as the one that was triggered. Looks like somebody tried to hack your system.',
140
                'actualCommand' => $actualCommand['command'],
141
                'exitCode' => Command::FAILURE
142
            ];
143
        }
144
    }
145
}
146