GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — develop (#334)
by Dane
05:31 queued 02:47
created

ServerRepository::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
/**
3
 * Pterodactyl - Panel
4
 * Copyright (c) 2015 - 2017 Dane Everitt <[email protected]>.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24
25
namespace Pterodactyl\Repositories;
26
27
use DB;
28
use Log;
29
use Crypt;
30
use Validator;
31
use Pterodactyl\Models;
32
use Pterodactyl\Services\UuidService;
33
use GuzzleHttp\Exception\TransferException;
34
use Pterodactyl\Services\DeploymentService;
35
use Pterodactyl\Exceptions\DisplayException;
36
use Pterodactyl\Exceptions\DisplayValidationException;
37
38
class ServerRepository
39
{
40
    protected $daemonPermissions = [
41
        's:*',
42
    ];
43
44
    public function __construct()
45
    {
46
        //
47
    }
48
49
    /**
50
     * Generates a SFTP username for a server given a server name.
51
     * format: mumble_67c7a4b0.
52
     *
53
     * @param  string $name
54
     * @param  string $identifier
55
     * @return string
56
     */
57
    protected function generateSFTPUsername($name, $identifier = null)
58
    {
59
        if (is_null($identifier) || ! ctype_alnum($identifier)) {
60
            $unique = str_random(8);
61
        } else {
62
            if (strlen($identifier) < 8) {
63
                $unique = $identifier . str_random((8 - strlen($identifier)));
64
            } else {
65
                $unique = substr($identifier, 0, 8);
66
            }
67
        }
68
69
        // Filter the Server Name
70
        $name = trim(preg_replace('/[^\w]+/', '', $name), '_');
71
        $name = (strlen($name) < 1) ? str_random(6) : $name;
72
73
        return strtolower(substr($name, 0, 6) . '_' . $unique);
74
    }
75
76
    /**
77
     * Adds a new server to the system.
78
     * @param   array  $data  An array of data descriptors for creating the server. These should align to the columns in the database.
79
     * @return  int
80
     */
81
    public function create(array $data)
82
    {
83
84
        // Validate Fields
85
        $validator = Validator::make($data, [
86
            'user_id' => 'required|exists:users,id',
87
            'name' => 'required|regex:/^([\w .-]{1,200})$/',
88
            'memory' => 'required|numeric|min:0',
89
            'swap' => 'required|numeric|min:-1',
90
            'io' => 'required|numeric|min:10|max:1000',
91
            'cpu' => 'required|numeric|min:0',
92
            'disk' => 'required|numeric|min:0',
93
            'service_id' => 'required|numeric|min:1|exists:services,id',
94
            'option_id' => 'required|numeric|min:1|exists:service_options,id',
95
            'location_id' => 'required|numeric|min:1|exists:locations,id',
96
            'pack_id' => 'sometimes|nullable|numeric|min:0',
97
            'startup' => 'string',
98
            'auto_deploy' => 'sometimes|boolean',
99
            'custom_id' => 'sometimes|required|numeric|unique:servers,id',
100
        ]);
101
102
        $validator->sometimes('node_id', 'required|numeric|min:1|exists:nodes,id', function ($input) {
103
            return ! ($input->auto_deploy);
104
        });
105
106
        $validator->sometimes('allocation_id', 'required|numeric|exists:allocations,id', function ($input) {
107
            return ! ($input->auto_deploy);
108
        });
109
110
        $validator->sometimes('allocation_additional.*', 'sometimes|required|numeric|exists:allocations,id', function ($input) {
111
            return ! ($input->auto_deploy);
112
        });
113
114
        // Run validator, throw catchable and displayable exception if it fails.
115
        // Exception includes a JSON result of failed validation rules.
116
        if ($validator->fails()) {
117
            throw new DisplayValidationException($validator->errors());
118
        }
119
120
        $user = Models\User::findOrFail($data['user_id']);
121
122
        $autoDeployed = false;
123
        if (isset($data['auto_deploy']) && $data['auto_deploy']) {
124
            // This is an auto-deployment situation
125
            // Ignore any other passed node data
126
            unset($data['node_id'], $data['allocation_id']);
127
128
            $autoDeployed = true;
129
            $node = DeploymentService::smartRandomNode($data['memory'], $data['disk'], $data['location_id']);
130
            $allocation = DeploymentService::randomAllocation($node->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Pterodactyl\Models\Node>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
131
        } else {
132
            $node = Models\Node::findOrFail($data['node_id']);
133
        }
134
135
        // Verify IP & Port are a.) free and b.) assigned to the node.
136
        // We know the node exists because of 'exists:nodes,id' in the validation
137
        if (! $autoDeployed) {
138
            $allocation = Models\Allocation::where('id', $data['allocation_id'])->where('node_id', $data['node_id'])->whereNull('server_id')->first();
139
        }
140
141
        // Something failed in the query, either that combo doesn't exist, or it is in use.
142
        if (! $allocation) {
0 ignored issues
show
Bug introduced by
The variable $allocation does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
143
            throw new DisplayException('The selected Allocation ID is either already in use, or unavaliable for this node.');
144
        }
145
146
        // Validate those Service Option Variables
147
        // We know the service and option exists because of the validation.
148
        // We need to verify that the option exists for the service, and then check for
149
        // any required variable fields. (fields are labeled env_<env_variable>)
150
        $option = Models\ServiceOption::where('id', $data['option_id'])->where('service_id', $data['service_id'])->first();
151
        if (! $option) {
152
            throw new DisplayException('The requested service option does not exist for the specified service.');
153
        }
154
155
        // Validate the Pack
156
        if (! isset($data['pack_id']) || (int) $data['pack_id'] < 1) {
157
            $data['pack_id'] = null;
158
        } else {
159
            $pack = Models\ServicePack::where('id', $data['pack_id'])->where('option_id', $data['option_id'])->first();
160
            if (! $pack) {
161
                throw new DisplayException('The requested service pack does not seem to exist for this combination.');
162
            }
163
        }
164
165
        // Load up the Service Information
166
        $service = Models\Service::find($option->service_id);
167
168
        // Check those Variables
169
        $variables = Models\ServiceVariable::where('option_id', $data['option_id'])->get();
170
        $variableList = [];
171
        if ($variables) {
172
            foreach ($variables as $variable) {
173
174
                // Is the variable required?
175
                if (! isset($data['env_' . $variable->env_variable])) {
176
                    if ($variable->required) {
177
                        throw new DisplayException('A required service option variable field (env_' . $variable->env_variable . ') was missing from the request.');
178
                    }
179
                    $variableList[] = [
180
                        'id' => $variable->id,
181
                        'env' => $variable->env_variable,
182
                        'val' => $variable->default_value,
183
                    ];
184
                    continue;
185
                }
186
187
                // Check aganist Regex Pattern
188
                if (! is_null($variable->regex) && ! preg_match($variable->regex, $data['env_' . $variable->env_variable])) {
189
                    throw new DisplayException('Failed to validate service option variable field (env_' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').');
190
                }
191
192
                $variableList[] = [
193
                    'id' => $variable->id,
194
                    'env' => $variable->env_variable,
195
                    'val' => $data['env_' . $variable->env_variable],
196
                ];
197
                continue;
198
            }
199
        }
200
201
        // Check Overallocation
202
        if (! $autoDeployed) {
203
            if (is_numeric($node->memory_overallocate) || is_numeric($node->disk_overallocate)) {
204
                $totals = Models\Server::select(DB::raw('SUM(memory) as memory, SUM(disk) as disk'))->where('node_id', $node->id)->first();
0 ignored issues
show
Bug introduced by
The method select() does not exist on Pterodactyl\Models\Server. Did you maybe mean addSelectsToQuery()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
205
206
                // Check memory limits
207 View Code Duplication
                if (is_numeric($node->memory_overallocate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
208
                    $newMemory = $totals->memory + $data['memory'];
209
                    $memoryLimit = ($node->memory * (1 + ($node->memory_overallocate / 100)));
210
                    if ($newMemory > $memoryLimit) {
211
                        throw new DisplayException('The amount of memory allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->memory_overallocate + 100) . '% of its assigned ' . $node->memory . 'Mb of memory (' . $memoryLimit . 'Mb) of which ' . (($totals->memory / $node->memory) * 100) . '% (' . $totals->memory . 'Mb) is in use already. By allocating this server the node would be at ' . (($newMemory / $node->memory) * 100) . '% (' . $newMemory . 'Mb) usage.');
212
                    }
213
                }
214
215
                // Check Disk Limits
216 View Code Duplication
                if (is_numeric($node->disk_overallocate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
217
                    $newDisk = $totals->disk + $data['disk'];
218
                    $diskLimit = ($node->disk * (1 + ($node->disk_overallocate / 100)));
219
                    if ($newDisk > $diskLimit) {
220
                        throw new DisplayException('The amount of disk allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->disk_overallocate + 100) . '% of its assigned ' . $node->disk . 'Mb of disk (' . $diskLimit . 'Mb) of which ' . (($totals->disk / $node->disk) * 100) . '% (' . $totals->disk . 'Mb) is in use already. By allocating this server the node would be at ' . (($newDisk / $node->disk) * 100) . '% (' . $newDisk . 'Mb) usage.');
221
                    }
222
                }
223
            }
224
        }
225
226
        DB::beginTransaction();
227
228
        try {
229
            $uuid = new UuidService;
230
231
            // Add Server to the Database
232
            $server = new Models\Server;
233
            $genUuid = $uuid->generate('servers', 'uuid');
234
            $genShortUuid = $uuid->generateShort('servers', 'uuidShort', $genUuid);
235
236
            if (isset($data['custom_id'])) {
237
                $server->id = $data['custom_id'];
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Pterodactyl\Models\Server>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
238
            }
239
240
            $server->fill([
241
                'uuid' => $genUuid,
242
                'uuidShort' => $genShortUuid,
243
                'node_id' => $node->id,
244
                'name' => $data['name'],
245
                'suspended' => 0,
246
                'owner_id' => $user->id,
247
                'memory' => $data['memory'],
248
                'swap' => $data['swap'],
249
                'disk' => $data['disk'],
250
                'io' => $data['io'],
251
                'cpu' => $data['cpu'],
252
                'oom_disabled' => (isset($data['oom_disabled'])) ? true : false,
253
                'allocation_id' => $allocation->id,
254
                'service_id' => $data['service_id'],
255
                'option_id' => $data['option_id'],
256
                'pack_id' => $data['pack_id'],
257
                'startup' => $data['startup'],
258
                'daemonSecret' => $uuid->generate('servers', 'daemonSecret'),
259
                'image' => (isset($data['custom_container'])) ? $data['custom_container'] : $option->docker_image,
260
                'username' => $this->generateSFTPUsername($data['name'], $genShortUuid),
261
                'sftp_password' => Crypt::encrypt('not set'),
262
            ]);
263
            $server->save();
264
265
            // Mark Allocation in Use
266
            $allocation->server_id = $server->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Pterodactyl\Models\Server>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
267
            $allocation->save();
268
269
            // Add Additional Allocations
270
            if (isset($data['allocation_additional']) && is_array($data['allocation_additional'])) {
271
                foreach ($data['allocation_additional'] as $allocation) {
272
                    $model = Models\Allocation::where('id', $allocation)->where('node_id', $data['node_id'])->whereNull('server_id')->first();
273
                    if (! $model) {
274
                        continue;
275
                    }
276
277
                    $model->server_id = $server->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Pterodactyl\Models\Server>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
278
                    $model->save();
279
                }
280
            }
281
282
            // Add Variables
283
            $environmentVariables = [
284
                'STARTUP' => $data['startup'],
285
            ];
286
287
            foreach ($variableList as $item) {
288
                $environmentVariables[$item['env']] = $item['val'];
289
290
                Models\ServerVariable::create([
291
                    'server_id' => $server->id,
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
292
                    'variable_id' => $item['id'],
293
                    'variable_value' => $item['val'],
294
                ]);
295
            }
296
297
            $server->load('allocation', 'allocations');
298
            $node->guzzleClient(['X-Access-Token' => $node->daemonSecret])->request('POST', '/servers', [
299
                'json' => [
300
                    'uuid' => (string) $server->uuid,
0 ignored issues
show
Documentation introduced by
The property uuid does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
301
                    'user' => $server->username,
0 ignored issues
show
Documentation introduced by
The property username does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
302
                    'build' => [
303
                        'default' => [
304
                            'ip' => $server->allocation->ip,
0 ignored issues
show
Documentation introduced by
The property allocation does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
305
                            'port' => $server->allocation->port,
0 ignored issues
show
Documentation introduced by
The property allocation does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
306
                        ],
307
                        'ports' => $server->allocations->groupBy('ip')->map(function ($item) {
0 ignored issues
show
Documentation introduced by
The property allocations does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
308
                            return $item->pluck('port');
309
                        })->toArray(),
310
                        'env' => $environmentVariables,
311
                        'memory' => (int) $server->memory,
0 ignored issues
show
Documentation introduced by
The property memory does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
312
                        'swap' => (int) $server->swap,
0 ignored issues
show
Documentation introduced by
The property swap does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
313
                        'io' => (int) $server->io,
0 ignored issues
show
Documentation introduced by
The property io does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
314
                        'cpu' => (int) $server->cpu,
0 ignored issues
show
Documentation introduced by
The property cpu does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
315
                        'disk' => (int) $server->disk,
0 ignored issues
show
Documentation introduced by
The property disk does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
316
                        'image' => (isset($data['custom_container'])) ? $data['custom_container'] : $option->docker_image,
317
                    ],
318
                    'service' => [
319
                        'type' => $service->file,
320
                        'option' => $option->tag,
321
                        'pack' => (isset($pack)) ? $pack->uuid : null,
322
                    ],
323
                    'keys' => [
324
                        (string) $server->daemonSecret => $this->daemonPermissions,
0 ignored issues
show
Documentation introduced by
The property daemonSecret does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
325
                    ],
326
                    'rebuild' => false,
327
                ],
328
            ]);
329
330
            DB::commit();
331
332
            return $server;
333
        } catch (TransferException $ex) {
334
            DB::rollBack();
335
            throw new DisplayException('There was an error while attempting to connect to the daemon to add this server.', $ex);
336
        } catch (\Exception $ex) {
337
            DB::rollBack();
338
            throw $ex;
339
        }
340
    }
341
342
    /**
343
     * [updateDetails description].
344
     * @param  int  $id
345
     * @param  array    $data
346
     * @return bool
347
     */
348
    public function updateDetails($id, array $data)
349
    {
350
        $uuid = new UuidService;
351
        $resetDaemonKey = false;
352
353
        // Validate Fields
354
        $validator = Validator::make($data, [
355
            'owner_id' => 'sometimes|required|integer|exists:users,id',
356
            'name' => 'sometimes|required|regex:([\w .-]{1,200})',
357
            'reset_token' => 'sometimes|required|accepted',
358
        ]);
359
360
        // Run validator, throw catchable and displayable exception if it fails.
361
        // Exception includes a JSON result of failed validation rules.
362
        if ($validator->fails()) {
363
            throw new DisplayValidationException($validator->errors());
364
        }
365
366
        DB::beginTransaction();
367
368
        try {
369
            $server = Models\Server::with('user')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
370
371
            // Update daemon secret if it was passed.
372
            if (isset($data['reset_token']) || (isset($data['owner_id']) && (int) $data['owner_id'] !== $server->user->id)) {
373
                $oldDaemonKey = $server->daemonSecret;
374
                $server->daemonSecret = $uuid->generate('servers', 'daemonSecret');
375
                $resetDaemonKey = true;
376
            }
377
378
            // Update Server Owner if it was passed.
379
            if (isset($data['owner_id']) && (int) $data['owner_id'] !== $server->user->id) {
380
                $server->owner_id = $data['owner_id'];
381
            }
382
383
            // Update Server Name if it was passed.
384
            if (isset($data['name'])) {
385
                $server->name = $data['name'];
386
            }
387
388
            // Save our changes
389
            $server->save();
390
391
            // Do we need to update? If not, return successful.
392
            if (! $resetDaemonKey) {
393
                DB::commit();
394
395
                return true;
396
            }
397
398
            $res = $server->node->guzzleClient([
399
                'X-Access-Server' => $server->uuid,
400
                'X-Access-Token' => $server->node->daemonSecret,
401
            ])->request('PATCH', '/server', [
402
                'exceptions' => false,
403
                'json' => [
404
                    'keys' => [
405
                        (string) $oldDaemonKey => [],
0 ignored issues
show
Bug introduced by
The variable $oldDaemonKey does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
406
                        (string) $server->daemonSecret => $this->daemonPermissions,
407
                    ],
408
                ],
409
            ]);
410
411
            if ($res->getStatusCode() === 204) {
412
                DB::commit();
413
414
                return true;
415
            } else {
416
                throw new DisplayException('Daemon returned a a non HTTP/204 error code. HTTP/' + $res->getStatusCode());
417
            }
418
        } catch (\Exception $ex) {
419
            DB::rollBack();
420
            Log::error($ex);
421
            throw new DisplayException('An error occured while attempting to update this server\'s information.');
422
        }
423
    }
424
425
    /**
426
     * [updateContainer description].
427
     * @param  int      $id
428
     * @param  array    $data
429
     * @return bool
430
     */
431 View Code Duplication
    public function updateContainer($id, array $data)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
432
    {
433
        $validator = Validator::make($data, [
434
            'docker_image' => 'required|string',
435
        ]);
436
437
        // Run validator, throw catchable and displayable exception if it fails.
438
        // Exception includes a JSON result of failed validation rules.
439
        if ($validator->fails()) {
440
            throw new DisplayValidationException($validator->errors());
441
        }
442
443
        DB::beginTransaction();
444
        try {
445
            $server = Models\Server::findOrFail($id);
446
447
            $server->image = $data['docker_image'];
448
            $server->save();
449
450
            $server->node->guzzleClient([
451
                'X-Access-Server' => $server->uuid,
452
                'X-Access-Token' => $server->node->daemonSecret,
453
            ])->request('PATCH', '/server', [
454
                'json' => [
455
                    'build' => [
456
                        'image' => $server->image,
457
                    ],
458
                ],
459
            ]);
460
461
            DB::commit();
462
463
            return true;
464
        } catch (TransferException $ex) {
465
            DB::rollBack();
466
            throw new DisplayException('A TransferException occured while attempting to update the container image. Is the daemon online? This error has been logged.', $ex);
467
        } catch (\Exception $ex) {
468
            DB::rollBack();
469
            throw $ex;
470
        }
471
    }
472
473
    /**
474
     * [changeBuild description].
475
     * @param  int  $id
476
     * @param  array    $data
477
     * @return bool
478
     */
479
    public function changeBuild($id, array $data)
480
    {
481
        $validator = Validator::make($data, [
482
            'allocation_id' => 'sometimes|required|exists:allocations,id',
483
            'add_allocations' => 'sometimes|required|array',
484
            'remove_allocations' => 'sometimes|required|array',
485
            'memory' => 'sometimes|required|integer|min:0',
486
            'swap' => 'sometimes|required|integer|min:-1',
487
            'io' => 'sometimes|required|integer|min:10|max:1000',
488
            'cpu' => 'sometimes|required|integer|min:0',
489
            'disk' => 'sometimes|required|integer|min:0',
490
        ]);
491
492
        // Run validator, throw catchable and displayable exception if it fails.
493
        // Exception includes a JSON result of failed validation rules.
494
        if ($validator->fails()) {
495
            throw new DisplayValidationException($validator->errors());
496
        }
497
498
        DB::beginTransaction();
499
500
        try {
501
            $server = Models\Server::with('allocation', 'allocations')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
502
            $newBuild = [];
503
            $newAllocations = [];
0 ignored issues
show
Unused Code introduced by
$newAllocations is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
504
505
            if (isset($data['allocation_id'])) {
506
                if ((int) $data['allocation_id'] !== $server->allocation_id) {
507
                    $selection = $server->allocations->where('id', $data['allocation_id'])->first();
508
                    if (! $selection) {
509
                        throw new DisplayException('The requested default connection is not allocated to this server.');
510
                    }
511
512
                    $server->allocation_id = $selection->id;
513
                    $newBuild['default'] = ['ip' => $selection->ip, 'port' => $selection->port];
514
515
                    $server->load('allocation');
516
                }
517
            }
518
519
            $newPorts = false;
520
            // Remove Assignments
521 View Code Duplication
            if (isset($data['remove_allocations'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
522
                foreach ($data['remove_allocations'] as $allocation) {
523
                    // Can't remove the assigned IP/Port combo
524
                    if ((int) $allocation === $server->allocation_id) {
525
                        continue;
526
                    }
527
528
                    $newPorts = true;
529
                    Models\Allocation::where('id', $allocation)->where('server_id', $server->id)->update([
530
                        'server_id' => null,
531
                    ]);
532
                }
533
534
                $server->load('allocations');
535
            }
536
537
            // Add Assignments
538 View Code Duplication
            if (isset($data['add_allocations'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
539
                foreach ($data['add_allocations'] as $allocation) {
540
                    $model = Models\Allocation::where('id', $allocation)->whereNull('server_id')->first();
541
                    if (! $model) {
542
                        continue;
543
                    }
544
545
                    $newPorts = true;
546
                    $model->update([
547
                        'server_id' => $server->id,
548
                    ]);
549
                }
550
551
                $server->load('allocations');
552
            }
553
554
            if ($newPorts) {
555
                $newBuild['ports|overwrite'] = $server->allocations->groupBy('ip')->map(function ($item) {
556
                    return $item->pluck('port');
557
                })->toArray();
558
            }
559
560
            // @TODO: verify that server can be set to this much memory without
561
            // going over node limits.
562 View Code Duplication
            if (isset($data['memory']) && $server->memory !== (int) $data['memory']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
563
                $server->memory = $data['memory'];
564
                $newBuild['memory'] = (int) $server->memory;
565
            }
566
567 View Code Duplication
            if (isset($data['swap']) && $server->swap !== (int) $data['swap']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
568
                $server->swap = $data['swap'];
569
                $newBuild['swap'] = (int) $server->swap;
570
            }
571
572
            // @TODO: verify that server can be set to this much disk without
573
            // going over node limits.
574 View Code Duplication
            if (isset($data['disk']) && $server->disk !== (int) $data['disk']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
575
                $server->disk = $data['disk'];
576
                $newBuild['disk'] = (int) $server->disk;
577
            }
578
579 View Code Duplication
            if (isset($data['cpu']) && $server->cpu !== (int) $data['cpu']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
580
                $server->cpu = $data['cpu'];
581
                $newBuild['cpu'] = (int) $server->cpu;
582
            }
583
584 View Code Duplication
            if (isset($data['io']) && $server->io !== (int) $data['io']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
585
                $server->io = $data['io'];
586
                $newBuild['io'] = (int) $server->io;
587
            }
588
589
            // Try save() here so if it fails we haven't contacted the daemon
590
            // This won't be committed unless the HTTP request succeedes anyways
591
            $server->save();
592
593
            if (! empty($newBuild)) {
594
                $server->node->guzzleClient([
595
                    'X-Access-Server' => $server->uuid,
596
                    'X-Access-Token' => $server->node->daemonSecret,
597
                ])->request('PATCH', '/server', [
598
                    'json' => [
599
                        'build' => $newBuild,
600
                    ],
601
                ]);
602
            }
603
604
            DB::commit();
605
606
            return $server;
607
        } catch (TransferException $ex) {
608
            DB::rollBack();
609
            throw new DisplayException('A TransferException occured while attempting to update the server configuration, check that the daemon is online. This error has been logged.', $ex);
610
        } catch (\Exception $ex) {
611
            DB::rollBack();
612
            throw $ex;
613
        }
614
    }
615
616
    public function updateStartup($id, array $data, $admin = false)
617
    {
618
        $server = Models\Server::with('variables', 'option.variables')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
619
620
        DB::transaction(function () use ($admin, $data, $server) {
621
            if (isset($data['startup']) && $admin) {
622
                $server->startup = $data['startup'];
623
                $server->save();
624
            }
625
626
            if ($server->option->variables) {
627
                foreach ($server->option->variables as &$variable) {
628
                    $set = isset($data['env_' . $variable->id]);
629
630
                    // Variable is required but was not passed into the function.
631
                    if ($variable->required && ! $set) {
632
                        throw new DisplayException('A required variable (' . $variable->env_variable . ') was not passed in the request.');
633
                    }
634
635
                    // If user is not an admin and are trying to edit a non-editable field
636
                    // or an invisible field just silently skip the variable.
637
                    if (! $admin && (! $variable->user_editable || ! $variable->user_viewable)) {
638
                        continue;
639
                    }
640
641
                    // Confirm value is valid when compared aganist regex.
642
                    // @TODO: switch to Laravel validation rules.
643
                    if ($set && ! is_null($variable->regex)) {
644
                        if (! preg_match($variable->regex, $data['env_' . $variable->id])) {
645
                            throw new DisplayException('The value passed for a variable (' . $variable->env_variable . ') could not be matched aganist the regex for that field (' . $variable->regex . ').');
646
                        }
647
                    }
648
649
                    $svar = Models\ServerVariable::firstOrNew([
650
                        'server_id' => $server->id,
651
                        'variable_id' => $variable->id,
652
                    ]);
653
654
                    // Set the value; if one was not passed set it to the default value
655
                    if ($set) {
656
                        $svar->variable_value = $data['env_' . $variable->id];
657
658
                    // Not passed, check if this record exists if so keep value, otherwise set default
659
                    } else {
660
                        $svar->variable_value = ($svar->exists) ? $svar->variable_value : $variable->default_value;
661
                    }
662
663
                    $svar->save();
664
                }
665
            }
666
667
            // Reload Variables
668
            $server->load('variables');
669
            $environment = $server->option->variables->map(function ($item, $key) use ($server) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
670
                $display = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
671
672
                return [
673
                    'variable' => $item->env_variable,
674
                    'value' => (! is_null($display)) ? $display : $item->default_value,
675
                ];
676
            });
677
678
            $server->node->guzzleClient([
679
                'X-Access-Server' => $server->uuid,
680
                'X-Access-Token' => $server->node->daemonSecret,
681
            ])->request('PATCH', '/server', [
682
                'json' => [
683
                    'build' => [
684
                        'env|overwrite' => $environment->pluck('value', 'variable')->merge(['STARTUP' => $server->startup]),
685
                    ],
686
                ],
687
            ]);
688
        });
689
    }
690
691 View Code Duplication
    public function queueDeletion($id, $force = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
692
    {
693
        $server = Models\Server::findOrFail($id);
694
        DB::beginTransaction();
695
696
        try {
697
            if ($force) {
698
                $server->installed = 3;
699
                $server->save();
700
            }
701
702
            $server->delete();
703
704
            return DB::commit();
705
        } catch (\Exception $ex) {
706
            DB::rollBack();
707
            throw $ex;
708
        }
709
    }
710
711
    public function delete($id, $force = false)
712
    {
713
        $server = Models\Server::withTrashed()->with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method withTrashed() does not exist on Pterodactyl\Models\Server. Did you maybe mean trashed()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
714
715
        // Handle server being restored previously or
716
        // an accidental queue.
717
        if (! $server->trashed()) {
718
            return;
719
        }
720
721
        DB::beginTransaction();
722
        try {
723
            // Unassign Allocations
724
            Models\Allocation::where('server_id', $server->id)->update([
725
                'server_id' => null,
726
            ]);
727
728
            // Remove Variables
729
            Models\ServerVariable::where('server_id', $server->id)->delete();
730
731
            // Remove SubUsers
732
            foreach (Models\Subuser::with('permissions')->where('server_id', $server->id)->get() as &$subuser) {
0 ignored issues
show
Bug introduced by
The method where does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Bug introduced by
The expression \Pterodactyl\Models\Subu...d', $server->id)->get() cannot be used as a reference.

Let?s assume that you have the following foreach statement:

foreach ($array as &$itemValue) { }

$itemValue is assigned by reference. This is possible because the expression (in the example $array) can be used as a reference target.

However, if we were to replace $array with something different like the result of a function call as in

foreach (getArray() as &$itemValue) { }

then assigning by reference is not possible anymore as there is no target that could be modified.

Available Fixes

1. Do not assign by reference
foreach (getArray() as $itemValue) { }
2. Assign to a local variable first
$array = getArray();
foreach ($array as &$itemValue) {}
3. Return a reference
function &getArray() { $array = array(); return $array; }

foreach (getArray() as &$itemValue) { }
Loading history...
733
                foreach ($subuser->permissions as &$permission) {
734
                    $permission->delete();
735
                }
736
                $subuser->delete();
737
            }
738
739
            // Remove Downloads
740
            Models\Download::where('server', $server->uuid)->delete();
741
742
            // Clear Tasks
743
            Models\Task::where('server', $server->id)->delete();
744
745
            // Delete Databases
746
            // This is the one un-recoverable point where
747
            // transactions will not save us.
748
            //
749
            // @TODO: move to post-deletion event as a queued task!
750
            // $repository = new DatabaseRepository;
751
            // foreach (Models\Database::select('id')->where('server_id', $server->id)->get() as &$database) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
752
            //     $repository->drop($database->id);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
753
            // }
754
755
            $server->node->guzzleClient([
756
                'X-Access-Token' => $server->node->daemonSecret,
757
                'X-Access-Server' => $server->uuid,
758
            ])->request('DELETE', '/servers');
759
760
            $server->forceDelete();
761
            DB::commit();
762
        } catch (TransferException $ex) {
763
            // Set installed is set to 3 when force deleting.
764
            if ($server->installed === 3 || $force) {
765
                $server->forceDelete();
766
                DB::commit();
767
            } else {
768
                DB::rollBack();
769
                throw $ex;
770
            }
771
        } catch (\Exception $ex) {
772
            DB::rollBack();
773
            throw $ex;
774
        }
775
    }
776
777
    public function cancelDeletion($id)
778
    {
779
        $server = Models\Server::withTrashed()->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method withTrashed() does not exist on Pterodactyl\Models\Server. Did you maybe mean trashed()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
780
        $server->restore();
781
782
        $server->installed = 1;
783
        $server->save();
784
    }
785
786
    public function toggleInstall($id)
787
    {
788
        $server = Models\Server::findOrFail($id);
789
        if ($server->installed > 1) {
790
            throw new DisplayException('This server was marked as having a failed install or being deleted, you cannot override this.');
791
        }
792
        $server->installed = ! $server->installed;
793
794
        return $server->save();
795
    }
796
797
    /**
798
     * Suspends a server instance making it unable to be booted or used by a user.
799
     * @param  int $id
800
     * @return bool
801
     */
802 View Code Duplication
    public function suspend($id, $deleted = false)
0 ignored issues
show
Unused Code introduced by
The parameter $deleted is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
803
    {
804
        $server = Models\Server::withTrashed()->with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method withTrashed() does not exist on Pterodactyl\Models\Server. Did you maybe mean trashed()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
805
806
        DB::beginTransaction();
807
808
        try {
809
810
            // Already suspended, no need to make more requests.
811
            if ($server->suspended) {
812
                return true;
813
            }
814
815
            $server->suspended = 1;
816
            $server->save();
817
818
            $server->node->guzzleClient([
819
                'X-Access-Token' => $server->node->daemonSecret,
820
                'X-Access-Server' => $server->uuid,
821
            ])->request('POST', '/server/suspend');
822
823
            return DB::commit();
824
        } catch (TransferException $ex) {
825
            DB::rollBack();
826
            throw new DisplayException('An error occured while attempting to contact the remote daemon to suspend this server.', $ex);
827
        } catch (\Exception $ex) {
828
            DB::rollBack();
829
            throw $ex;
830
        }
831
    }
832
833
    /**
834
     * Unsuspends a server instance.
835
     * @param  int $id
836
     * @return bool
837
     */
838 View Code Duplication
    public function unsuspend($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
839
    {
840
        $server = Models\Server::with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
841
842
        DB::beginTransaction();
843
844
        try {
845
846
            // Already unsuspended, no need to make more requests.
847
            if ($server->suspended === 0) {
848
                return true;
849
            }
850
851
            $server->suspended = 0;
852
            $server->save();
853
854
            $server->node->guzzleClient([
855
                'X-Access-Token' => $server->node->daemonSecret,
856
                'X-Access-Server' => $server->uuid,
857
            ])->request('POST', '/server/unsuspend');
858
859
            return DB::commit();
860
        } catch (TransferException $ex) {
861
            DB::rollBack();
862
            throw new DisplayException('An error occured while attempting to contact the remote daemon to un-suspend this server.', $ex);
863
        } catch (\Exception $ex) {
864
            DB::rollBack();
865
            throw $ex;
866
        }
867
    }
868
869 View Code Duplication
    public function updateSFTPPassword($id, $password)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
870
    {
871
        $server = Models\Server::with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
872
873
        $validator = Validator::make(['password' => $password], [
874
            'password' => 'required|regex:/^((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})$/',
875
        ]);
876
877
        if ($validator->fails()) {
878
            throw new DisplayValidationException(json_encode($validator->errors()));
879
        }
880
881
        DB::beginTransaction();
882
        $server->sftp_password = Crypt::encrypt($password);
883
884
        try {
885
            $server->save();
886
887
            $server->node->guzzleClient([
888
                'X-Access-Token' => $server->node->daemonSecret,
889
                'X-Access-Server' => $server->uuid,
890
            ])->request('POST', '/server/password', [
891
                'json' => ['password' => $password],
892
            ]);
893
894
            DB::commit();
895
896
            return true;
897
        } catch (TransferException $ex) {
898
            DB::rollBack();
899
            throw new DisplayException('There was an error while attmping to contact the remote service to change the password.', $ex);
900
        } catch (\Exception $ex) {
901
            DB::rollBack();
902
            throw $ex;
903
        }
904
    }
905
}
906