Passed
Push — master ( 59b0b6...e4937f )
by Nils
02:36
created

RemoteConnect::run()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 63
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 8
eloc 42
c 4
b 0
f 0
nc 6
nop 2
dl 0
loc 63
rs 8.0035

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Startwind\Inventorio\Remote;
4
5
use GuzzleHttp\Client;
6
use GuzzleHttp\RequestOptions;
7
use Symfony\Component\Console\Command\Command;
8
use Symfony\Component\Process\Process;
9
10
class RemoteConnect
11
{
12
    private const URL_HAS_COMMAND = '/inventorio/command/queued/{serverId}';
13
    private const URL_POP_COMMAND = '/inventorio/command/pop/{serverId}';
14
    private const URL_SEND_OUTPUT = '/inventorio/command/result/{commandId}';
15
16
    private string $inventorioServer;
17
    private string $serverId;
18
    private array $commands;
19
    private string $secret;
20
21
    public function __construct(string $inventorioServer, string $serverId, array $commands, string $secret)
22
    {
23
        $this->inventorioServer = $inventorioServer;
24
        $this->serverId = $serverId;
25
        $this->commands = $commands;
26
        $this->secret = $secret;
27
    }
28
29
    public function run($remoteEnabled, $smartCareEnabled): string
30
    {
31
        $client = new Client();
32
33
        $popUrl = str_replace('{serverId}', $this->serverId, self::URL_POP_COMMAND);
34
        $hasUrl = str_replace('{serverId}', $this->serverId, self::URL_HAS_COMMAND);
35
36
        $response = $client->get($this->inventorioServer . $hasUrl);
37
        $result = json_decode($response->getBody(), true);
38
39
        if ($result['data']['hasQueued']) {
40
            $commandResponse = $client->get($this->inventorioServer . $popUrl);
41
            $commandResult = json_decode($commandResponse->getBody(), true);
42
43
            if ($commandResult['type'] === 'smartCare' || !$smartCareEnabled) {
44
                $commandOutput = [
45
                    "output" => '',
46
                    'error' => 'SmartCare is not activated on this server',
47
                    'actualCommand' => '<unknown>',
48
                    'exitCode' => Command::FAILURE
49
                ];
50
            } elseif ($commandResult['type'] === 'remote' || !$remoteEnabled) {
51
                $commandOutput = [
52
                    "output" => '',
53
                    'error' => 'Remote commands are not enabled on this server',
54
                    'actualCommand' => '<unknown>',
55
                    'exitCode' => Command::FAILURE
56
                ];
57
            } else {
58
                if ($commandResult['type'] === 'remote') {
59
60
                    $commandId = $commandResult['data']['command']['command'];
61
62
                    $expectedProof = md5($commandId . $this->secret);
63
                    $cloudProof = $commandResult['data']['command']['proof'];
64
65
                    if ($expectedProof !== $cloudProof) {
66
                        $cloudCommand = $commandResult['data']['command']['storedCommand']['command'];
67
                        $identifier = $commandResult['data']['command']['id'];
68
                        $commandOutput = $this->runCommand($commandId, $cloudCommand);
69
                    } else {
70
                        $commandOutput = [
71
                            "output" => '',
72
                            'error' => 'The authenticity of the job could not be verified.',
73
                            'actualCommand' => '<unknown>',
74
                            'exitCode' => Command::FAILURE
75
                        ];
76
                    }
77
                } else {
78
                    var_dump($commandResult);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($commandResult) looks like debug code. Are you sure you do not want to remove it?
Loading history...
79
                }
80
            }
81
82
            $sendUrl = str_replace('{commandId}', $identifier, self::URL_SEND_OUTPUT);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $identifier does not seem to be defined for all execution paths leading up to this point.
Loading history...
83
84
            $client->post($this->inventorioServer . $sendUrl, [
85
                RequestOptions::JSON => ['output' => $commandOutput]
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $commandOutput does not seem to be defined for all execution paths leading up to this point.
Loading history...
86
            ]);
87
88
            return 'Command: ' . $commandOutput['actualCommand'];
89
        }
90
91
        return "";
92
    }
93
94
    private function runCommand(string $command, string $cloudCommand): array
95
    {
96
        if (!array_key_exists($command, $this->commands)) {
97
            return [
98
                "output" => '',
99
                "error" => "No command with identifier '" . $command . "' found.",
100
                'actualCommand' => '<unknown>',
101
                'exitCode' => Command::FAILURE
102
            ];
103
        }
104
105
        $actualCommand = $this->commands[$command];
106
107
        if ($cloudCommand === $actualCommand['command']) {
108
            $shellCommandLine = "timeout --kill-after=5s 1m " . $actualCommand['command'];
109
            $process = Process::fromShellCommandline($shellCommandLine);
110
            $process->run();
111
112
            return [
113
                'output' => $process->getOutput(),
114
                'error' => $process->getErrorOutput(),
115
                'actualCommand' => $actualCommand['command'],
116
                'exitCode' => $process->getExitCode()
117
            ];
118
        } else {
119
            return [
120
                'output' => '',
121
                '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.',
122
                'actualCommand' => $actualCommand['command'],
123
                'exitCode' => Command::FAILURE
124
            ];
125
        }
126
    }
127
128
}
129