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
Push — develop ( a1d3bb...b96e5a )
by Dane
02:57
created

SubuserRepository::update()   C

Complexity

Conditions 7
Paths 68

Size

Total Lines 68
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 68
rs 6.9654
c 0
b 0
f 0
cc 7
eloc 41
nc 68
nop 2

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Pterodactyl - Panel
4
 * Copyright (c) 2015 - 2016 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 int          Returns the ID of the newly created subuser.
116
     */
117
    public function create($sid, array $data)
118
    {
119
        $server = Models\Server::findOrFail($sid);
120
        $validator = Validator::make($data, [
121
            'permissions' => 'required|array',
122
            'email' => 'required|email',
123
        ]);
124
125
        if ($validator->fails()) {
126
            throw new DisplayValidationException(json_encode($validator->errors()));
127
        }
128
129
        DB::beginTransaction();
130
131
        try {
132
            // Determine if this user exists or if we need to make them an account.
133
            $user = Models\User::where('email', $data['email'])->first();
134
            if (! $user) {
135
                $password = str_random(16);
136
                try {
137
                    $repo = new UserRepository;
138
                    $uid = $repo->create($data['email'], $password);
139
                    $user = Models\User::findOrFail($uid);
140
                } catch (\Exception $ex) {
141
                    throw $ex;
142
                }
143
            }
144
145
            $uuid = new UuidService;
146
147
            $subuser = new Models\Subuser;
148
            $subuser->fill([
149
                'user_id' => $user->id,
150
                'server_id' => $server->id,
151
                'daemonSecret' => (string) $uuid->generate('servers', 'uuid'),
152
            ]);
153
            $subuser->save();
154
155
            $daemonPermissions = $this->coreDaemonPermissions;
156
            foreach ($data['permissions'] as $permission) {
157
                if (array_key_exists($permission, $this->permissions)) {
158
                    // Build the daemon permissions array for sending.
159
                    if (! is_null($this->permissions[$permission])) {
160
                        array_push($daemonPermissions, $this->permissions[$permission]);
161
                    }
162
                    $model = new Models\Permission;
163
                    $model->fill([
164
                        'user_id' => $user->id,
165
                        'server_id' => $server->id,
166
                        'permission' => $permission,
167
                    ]);
168
                    $model->save();
169
                }
170
            }
171
172
            // Contact Daemon
173
            // We contact even if they don't have any daemon permissions to overwrite
174
            // if they did have them previously.
175
176
            $node = Models\Node::getByID($server->node);
177
            $client = Models\Node::guzzleRequest($server->node);
178
179
            $res = $client->request('PATCH', '/server', [
0 ignored issues
show
Unused Code introduced by
$res 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...
180
                'headers' => [
181
                    'X-Access-Server' => $server->uuid,
182
                    'X-Access-Token' => $node->daemonSecret,
183
                ],
184
                'json' => [
185
                    'keys' => [
186
                        $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...
187
                    ],
188
                ],
189
            ]);
190
191
            $email = $data['email'];
192
            Mail::queue('emails.added-subuser', [
193
                'serverName' => $server->name,
194
                'url' => route('server.index', $server->uuidShort),
195
            ], function ($message) use ($email) {
196
                $message->to($email);
197
                $message->from(Settings::get('email_from', env('MAIL_FROM')), Settings::get('email_sender_name', env('MAIL_FROM_NAME', 'Pterodactyl Panel')));
198
                $message->subject(Settings::get('company') . ' - Added to Server');
199
            });
200
            DB::commit();
201
202
            return $subuser->id;
0 ignored issues
show
Documentation introduced by
The property id 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...
203
        } catch (\GuzzleHttp\Exception\TransferException $ex) {
204
            DB::rollBack();
205
            throw new DisplayException('There was an error attempting to connect to the daemon to add this user.', $ex);
206
        } catch (\Exception $ex) {
207
            DB::rollBack();
208
            throw $ex;
209
        }
210
211
        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...
212
    }
213
214
    /**
215
     * Revokes a users permissions on a server.
216
     * @param  int  $id  The ID of the subuser row in MySQL.
217
     * @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...
218
     * @throws DisplayValidationException
219
     * @throws DisplayException
220
     * @return void
221
     */
222
    public function delete($id)
223
    {
224
        $subuser = Models\Subuser::findOrFail($id);
225
        $server = Models\Server::findOrFail($subuser->server_id);
226
227
        DB::beginTransaction();
228
229
        try {
230
            Models\Permission::where('user_id', $subuser->user_id)->where('server_id', $subuser->server_id)->delete();
231
232
            $node = Models\Node::getByID($server->node);
233
            $client = Models\Node::guzzleRequest($server->node);
234
235
            $res = $client->request('PATCH', '/server', [
0 ignored issues
show
Unused Code introduced by
$res 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...
236
                'headers' => [
237
                    'X-Access-Server' => $server->uuid,
238
                    'X-Access-Token' => $node->daemonSecret,
239
                ],
240
                'json' => [
241
                    'keys' => [
242
                        $subuser->daemonSecret => [],
243
                    ],
244
                ],
245
            ]);
246
247
            $subuser->delete();
248
            DB::commit();
249
250
            return true;
251
        } catch (\GuzzleHttp\Exception\TransferException $ex) {
252
            DB::rollBack();
253
            throw new DisplayException('There was an error attempting to connect to the daemon to delete this subuser.', $ex);
254
        } catch (\Exception $ex) {
255
            DB::rollBack();
256
            throw $ex;
257
        }
258
259
        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...
260
    }
261
262
    /**
263
     * Updates permissions for a given subuser.
264
     * @param  int $id  The ID of the subuser row in MySQL. (Not the user ID)
265
     * @param  array  $data
266
     * @throws DisplayValidationException
267
     * @throws DisplayException
268
     * @return void
269
     */
270
    public function update($id, array $data)
271
    {
272
        $validator = Validator::make($data, [
273
            'permissions' => 'required|array',
274
            'user' => 'required|exists:users,id',
275
            'server' => 'required|exists:servers,id',
276
        ]);
277
278
        if ($validator->fails()) {
279
            throw new DisplayValidationException(json_encode($validator->all()));
280
        }
281
282
        $subuser = Models\Subuser::findOrFail($id);
283
        $server = Models\Server::findOrFail($data['server']);
284
285
        DB::beginTransaction();
286
287
        try {
288
            Models\Permission::where('user_id', $subuser->user_id)->where('server_id', $subuser->server_id)->delete();
289
290
            $daemonPermissions = $this->coreDaemonPermissions;
291
            foreach ($data['permissions'] as $permission) {
292
                if (array_key_exists($permission, $this->permissions)) {
293
                    // Build the daemon permissions array for sending.
294
                    if (! is_null($this->permissions[$permission])) {
295
                        array_push($daemonPermissions, $this->permissions[$permission]);
296
                    }
297
                    $model = new Models\Permission;
298
                    $model->fill([
299
                        'user_id' => $data['user'],
300
                        'server_id' => $data['server'],
301
                        'permission' => $permission,
302
                    ]);
303
                    $model->save();
304
                }
305
            }
306
307
            // Contact Daemon
308
            // We contact even if they don't have any daemon permissions to overwrite
309
            // if they did have them previously.
310
            $node = Models\Node::getByID($server->node);
311
            $client = Models\Node::guzzleRequest($server->node);
312
313
            $res = $client->request('PATCH', '/server', [
0 ignored issues
show
Unused Code introduced by
$res 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...
314
                'headers' => [
315
                    'X-Access-Server' => $server->uuid,
316
                    'X-Access-Token' => $node->daemonSecret,
317
                ],
318
                'json' => [
319
                    'keys' => [
320
                        $subuser->daemonSecret => $daemonPermissions,
321
                    ],
322
                ],
323
            ]);
324
325
            DB::commit();
326
327
            return true;
328
        } catch (\GuzzleHttp\Exception\TransferException $ex) {
329
            DB::rollBack();
330
            throw new DisplayException('There was an error attempting to connect to the daemon to update permissions.', $ex);
331
        } catch (\Exception $ex) {
332
            DB::rollBack();
333
            throw $ex;
334
        }
335
336
        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...
337
    }
338
}
339