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 (#286)
by Dane
03:00
created

SubuserRepository::create()   C

Complexity

Conditions 11
Paths 105

Size

Total Lines 88
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 88
rs 5.184
c 0
b 0
f 0
cc 11
eloc 55
nc 105
nop 2

How to fix   Long Method    Complexity   

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
 * 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 Mail;
29
use Settings;
30
use Validator;
31
use Pterodactyl\Models;
32
use Pterodactyl\Services\UuidService;
33
use Pterodactyl\Exceptions\DisplayException;
34
use Pterodactyl\Exceptions\DisplayValidationException;
35
36
class SubuserRepository
37
{
38
    /**
39
     * Core permissions required for every subuser on the daemon.
40
     * Without this we cannot connect the websocket or get basic
41
     * information about the server.
42
     * @var array
43
     */
44
    protected $coreDaemonPermissions = [
45
        's:get',
46
        's:console',
47
    ];
48
49
    /**
50
     * Allowed permissions and their related daemon permission.
51
     * @var array
52
     */
53
    protected $permissions = [
54
        // Power Permissions
55
        'power-start' => 's:power:start',
56
        'power-stop' => 's:power:stop',
57
        'power-restart' => 's:power:restart',
58
        'power-kill' => 's:power:kill',
59
60
        // Commands
61
        'send-command' => 's:command',
62
63
        // File Manager
64
        'list-files' => 's:files:get',
65
        'edit-files' => 's:files:read',
66
        'save-files' => 's:files:post',
67
        'create-files' => 's:files:post',
68
        'download-files' => null,
69
        'upload-files' => 's:files:upload',
70
        'delete-files' => 's:files:delete',
71
        'move-files' => 's:files:move',
72
        'copy-files' => 's:files:copy',
73
        'compress-files' => 's:files:compress',
74
        'decompress-files' => 's:files:decompress',
75
76
        // Subusers
77
        'list-subusers' => null,
78
        'view-subuser' => null,
79
        'edit-subuser' => null,
80
        'create-subuser' => null,
81
        'delete-subuser' => null,
82
83
        // Tasks
84
        'list-tasks' => null,
85
        'view-task' => null,
86
        'toggle-task' => null,
87
        'delete-task' => null,
88
        'create-task' => null,
89
        'queue-task' => null,
90
91
        // Management
92
        'set-connection' => null,
93
        'view-startup' => null,
94
        'edit-startup' => null,
95
        'view-sftp' => null,
96
        'reset-sftp' => 's:set-password',
97
        'view-sftp-password' => null,
98
99
        // Databases
100
        'view-databases' => null,
101
        'reset-db-password' => null,
102
    ];
103
104
    public function __construct()
105
    {
106
        //
107
    }
108
109
    /**
110
     * Creates a new subuser on the server.
111
     * @param  int $id     The ID of the server to add this subuser to.
0 ignored issues
show
Bug introduced by
There is no parameter named $id. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
112
     * @param  array  $data
113
     * @throws DisplayValidationException
114
     * @throws DisplayException
115
     * @return \Pterodactyl\Models\Subuser
116
     */
117
    public function create($sid, array $data)
118
    {
119
        $server = Models\Server::with('node')->findOrFail($sid);
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...
120
121
        $validator = Validator::make($data, [
122
            'permissions' => 'required|array',
123
            'email' => 'required|email',
124
        ]);
125
126
        if ($validator->fails()) {
127
            throw new DisplayValidationException(json_encode($validator->errors()));
128
        }
129
130
        DB::beginTransaction();
131
132
        try {
133
            // Determine if this user exists or if we need to make them an account.
134
            $user = Models\User::where('email', $data['email'])->first();
135
            if (! $user) {
136
                try {
137
                    $repo = new UserRepository;
138
                    $user = $repo->create([
139
                        'email' => $data['email'],
140
                        'username' => str_random(8),
141
                        'name_first' => 'Unassigned',
142
                        'name_last' => 'Name',
143
                        'root_admin' => false,
144
                    ]);
145
                } catch (\Exception $ex) {
146
                    throw $ex;
147
                }
148
            } elseif ($server->owner_id === $user->id) {
149
                throw new DisplayException('You cannot add the owner of a server as a subuser.');
150
            } elseif (Models\Subuser::select('id')->where('user_id', $user->id)->where('server_id', $server->id)->first()) {
151
                throw new DisplayException('A subuser with that email already exists for this server.');
152
            }
153
154
            $uuid = new UuidService;
155
            $subuser = Models\Subuser::create([
156
                'user_id' => $user->id,
157
                'server_id' => $server->id,
158
                'daemonSecret' => (string) $uuid->generate('servers', 'uuid'),
159
            ]);
160
161
            $daemonPermissions = $this->coreDaemonPermissions;
162
            foreach ($data['permissions'] as $permission) {
163
                if (array_key_exists($permission, $this->permissions)) {
164
                    // Build the daemon permissions array for sending.
165
                    if (! is_null($this->permissions[$permission])) {
166
                        array_push($daemonPermissions, $this->permissions[$permission]);
167
                    }
168
169
                    Models\Permission::create([
170
                        'user_id' => $user->id,
171
                        'server_id' => $server->id,
172
                        'permission' => $permission,
173
                    ]);
174
                }
175
            }
176
177
            // Contact Daemon
178
            // We contact even if they don't have any daemon permissions to overwrite
179
            // if they did have them previously.
180
181
            $server->node->guzzleClient([
182
                'X-Access-Server' => $server->uuid,
183
                'X-Access-Token' => $node->daemonSecret,
0 ignored issues
show
Bug introduced by
The variable $node does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
184
            ])->request('PATCH', '/server', [
185
                'json' => [
186
                    'keys' => [
187
                        $subuser->daemonSecret => $daemonPermissions,
0 ignored issues
show
Documentation introduced by
The property daemonSecret does not exist on object<Pterodactyl\Models\Subuser>. 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...
188
                    ],
189
                ],
190
            ]);
191
192
            DB::commit();
193
194
            return $subuser;
195
        } catch (\GuzzleHttp\Exception\TransferException $ex) {
196
            DB::rollBack();
197
            throw new DisplayException('There was an error attempting to connect to the daemon to add this user.', $ex);
198
        } catch (\Exception $ex) {
199
            DB::rollBack();
200
            throw $ex;
201
        }
202
203
        return false;
0 ignored issues
show
Unused Code introduced by
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
204
    }
205
206
    /**
207
     * Revokes a users permissions on a server.
208
     * @param  int  $id  The ID of the subuser row in MySQL.
209
     * @param  array    $data
0 ignored issues
show
Bug introduced by
There is no parameter named $data. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
210
     * @throws DisplayValidationException
211
     * @throws DisplayException
212
     * @return void
213
     */
214
    public function delete($id)
215
    {
216
        $subuser = Models\Subuser::with('server.node', 'permissions')->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...
217
        $server = $subuser->server;
218
219
        DB::beginTransaction();
220
221
        try {
222
            Models\Permission::where('user_id', $subuser->user_id)->where('server_id', $subuser->server_id)->delete();
223
224
            $server->node->guzzleClient([
225
                'X-Access-Server' => $server->uuid,
226
                'X-Access-Token' => $server->node->daemonSecret,
227
            ])->request('PATCH', '/server', [
228
                'json' => [
229
                    'keys' => [
230
                        $subuser->daemonSecret => [],
231
                    ],
232
                ],
233
            ]);
234
235
            $subuser->delete();
236
            DB::commit();
237
238
            return true;
239
        } catch (\GuzzleHttp\Exception\TransferException $ex) {
240
            DB::rollBack();
241
            throw new DisplayException('There was an error attempting to connect to the daemon to delete this subuser.', $ex);
242
        } catch (\Exception $ex) {
243
            DB::rollBack();
244
            throw $ex;
245
        }
246
247
        return false;
0 ignored issues
show
Unused Code introduced by
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
248
    }
249
250
    /**
251
     * Updates permissions for a given subuser.
252
     * @param  int $id  The ID of the subuser row in MySQL. (Not the user ID)
253
     * @param  array  $data
254
     * @throws DisplayValidationException
255
     * @throws DisplayException
256
     * @return void
257
     */
258
    public function update($id, array $data)
259
    {
260
        $validator = Validator::make($data, [
261
            'permissions' => 'required|array',
262
            'user' => 'required|exists:users,id',
263
            'server' => 'required|exists:servers,id',
264
        ]);
265
266
        if ($validator->fails()) {
267
            throw new DisplayValidationException(json_encode($validator->all()));
268
        }
269
270
        $subuser = Models\Subuser::with('server.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...
271
        $server = $subuser->server;
272
273
        DB::beginTransaction();
274
275
        try {
276
            Models\Permission::where('user_id', $subuser->user_id)->where('server_id', $subuser->server_id)->delete();
277
278
            $daemonPermissions = $this->coreDaemonPermissions;
279
            foreach ($data['permissions'] as $permission) {
280
                if (array_key_exists($permission, $this->permissions)) {
281
                    // Build the daemon permissions array for sending.
282
                    if (! is_null($this->permissions[$permission])) {
283
                        array_push($daemonPermissions, $this->permissions[$permission]);
284
                    }
285
                    $model = new Models\Permission;
286
                    $model->fill([
287
                        'user_id' => $data['user'],
288
                        'server_id' => $data['server'],
289
                        'permission' => $permission,
290
                    ]);
291
                    $model->save();
292
                }
293
            }
294
295
            // Contact Daemon
296
            // We contact even if they don't have any daemon permissions to overwrite
297
            // if they did have them previously.
298
            $server->node->guzzleClient([
299
                'X-Access-Server' => $server->uuid,
300
                'X-Access-Token' => $server->node->daemonSecret,
301
            ])->request('PATCH', '/server', [
302
                'json' => [
303
                    'keys' => [
304
                        $subuser->daemonSecret => $daemonPermissions,
305
                    ],
306
                ],
307
            ]);
308
309
            DB::commit();
310
311
            return true;
312
        } catch (\GuzzleHttp\Exception\TransferException $ex) {
313
            DB::rollBack();
314
            throw new DisplayException('There was an error attempting to connect to the daemon to update permissions.', $ex);
315
        } catch (\Exception $ex) {
316
            DB::rollBack();
317
            throw $ex;
318
        }
319
320
        return false;
0 ignored issues
show
Unused Code introduced by
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
321
    }
322
}
323