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.

DatabaseRepository   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 249
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 18
c 0
b 0
f 0
lcom 1
cbo 4
dl 0
loc 249
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
B create() 0 69 6
B password() 0 26 1
A drop() 0 13 1
A delete() 0 10 2
B add() 0 39 4
B update() 0 34 4
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 Crypt;
29
use Validator;
30
use Pterodactyl\Models\Server;
31
use Pterodactyl\Models\Database;
32
use Pterodactyl\Models\DatabaseHost;
33
use Pterodactyl\Exceptions\DisplayException;
34
use Pterodactyl\Exceptions\DisplayValidationException;
35
36
class DatabaseRepository
37
{
38
    /**
39
     * Adds a new database to a specified database host server.
40
     *
41
     * @param  int    $id
42
     * @param  array  $data
43
     * @return \Pterodactyl\Models\Database
44
     *
45
     * @throws \Pterodactyl\Exceptions\DisplayException
46
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
47
     */
48
    public function create($id, array $data)
49
    {
50
        $server = Server::findOrFail($id);
51
52
        $validator = Validator::make($data, [
53
            'host' => 'required|exists:database_hosts,id',
54
            'database' => 'required|regex:/^\w{1,100}$/',
55
            'connection' => 'required|regex:/^[0-9%.]{1,15}$/',
56
        ]);
57
58
        if ($validator->fails()) {
59
            throw new DisplayValidationException(json_encode($validator->errors()));
60
        }
61
62
        $host = DatabaseHost::findOrFail($data['host']);
63
        DB::beginTransaction();
64
65
        try {
66
            $database = Database::firstOrNew([
67
                'server_id' => $server->id,
68
                'database_host_id' => $data['host'],
69
                'database' => sprintf('s%d_%s', $server->id, $data['database']),
70
            ]);
71
72
            if ($database->exists) {
73
                throw new DisplayException('A database with those details already exists in the system.');
74
            }
75
76
            $database->username = sprintf('s%d_%s', $server->id, str_random(10));
77
            $database->remote = $data['connection'];
78
            $database->password = Crypt::encrypt(str_random(20));
79
80
            $database->save();
81
        } catch (\Exception $ex) {
82
            DB::rollBack();
83
            throw $ex;
84
        }
85
86
        try {
87
            $host->setDynamicConnection();
88
89
            DB::connection('dynamic')->statement(sprintf('CREATE DATABASE IF NOT EXISTS `%s`', $database->database));
90
            DB::connection('dynamic')->statement(sprintf(
91
                'CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'',
92
                $database->username, $database->remote, Crypt::decrypt($database->password)
93
            ));
94
            DB::connection('dynamic')->statement(sprintf(
95
                'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX ON `%s`.* TO `%s`@`%s`',
96
                $database->database, $database->username, $database->remote
97
            ));
98
99
            DB::connection('dynamic')->statement('FLUSH PRIVILEGES');
100
101
            // Save Everything
102
            DB::commit();
103
104
            return $database;
105
        } catch (\Exception $ex) {
106
            try {
107
                DB::connection('dynamic')->statement(sprintf('DROP DATABASE IF EXISTS `%s`', $database->database));
108
                DB::connection('dynamic')->statement(sprintf('DROP USER IF EXISTS `%s`@`%s`', $database->username, $database->remote));
109
                DB::connection('dynamic')->statement('FLUSH PRIVILEGES');
110
            } catch (\Exception $ex) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
111
            }
112
113
            DB::rollBack();
114
            throw $ex;
115
        }
116
    }
117
118
    /**
119
     * Updates the password for a given database.
120
     *
121
     * @param  int     $id
122
     * @param  string  $password
123
     * @return void
124
     *
125
     * @todo   Fix logic behind resetting passwords.
126
     */
127
    public function password($id, $password)
128
    {
129
        $database = Database::with('host')->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...
130
        $database->host->setDynamicConnection();
131
132
        DB::transaction(function () use ($database, $password) {
133
            $database->password = Crypt::encrypt($password);
134
135
            // We have to do the whole delete user, create user thing rather than
136
            // SET PASSWORD ... because MariaDB and PHP statements ends up inserting
137
            // a corrupted password. A way around this is strtoupper(sha1(sha1($password, true)))
138
            // but no garuntees that will work correctly with every system.
139
            DB::connection('dynamic')->statement(sprintf('DROP USER IF EXISTS `%s`@`%s`', $database->username, $database->remote));
140
            DB::connection('dynamic')->statement(sprintf(
141
                'CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'',
142
                $database->username, $database->remote, $password
143
            ));
144
            DB::connection('dynamic')->statement(sprintf(
145
                'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX ON `%s`.* TO `%s`@`%s`',
146
                $database->database, $database->username, $database->remote
147
            ));
148
            DB::connection('dynamic')->statement('FLUSH PRIVILEGES');
149
150
            $database->save();
151
        });
152
    }
153
154
    /**
155
     * Drops a database from the associated database host.
156
     *
157
     * @param  int  $id
158
     * @return void
159
     */
160
    public function drop($id)
161
    {
162
        $database = Database::with('host')->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...
163
        $database->host->setDynamicConnection();
164
165
        DB::transaction(function () use ($database) {
166
            DB::connection('dynamic')->statement(sprintf('DROP DATABASE IF EXISTS `%s`', $database->database));
167
            DB::connection('dynamic')->statement(sprintf('DROP USER IF EXISTS `%s`@`%s`', $database->username, $database->remote));
168
            DB::connection('dynamic')->statement('FLUSH PRIVILEGES');
169
170
            $database->delete();
171
        });
172
    }
173
174
    /**
175
     * Deletes a database host from the system if it has no associated databases.
176
     *
177
     * @param  int  $id
178
     * @return void
179
     *
180
     * @throws \Pterodactyl\Exceptions\DisplayException
181
     */
182
    public function delete($id)
183
    {
184
        $host = DatabaseHost::withCount('databases')->findOrFail($id);
185
186
        if ($host->databases_count > 0) {
187
            throw new DisplayException('You cannot delete a database host that has active databases attached to it.');
188
        }
189
190
        $host->delete();
191
    }
192
193
    /**
194
     * Adds a new Database Host to the system.
195
     *
196
     * @param  array  $data
197
     * @return \Pterodactyl\Models\DatabaseHost
198
     *
199
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
200
     */
201
    public function add(array $data)
202
    {
203
        if (isset($data['host'])) {
204
            $data['host'] = gethostbyname($data['host']);
205
        }
206
207
        $validator = Validator::make($data, [
208
            'name' => 'required|string|max:255',
209
            'host' => 'required|ip|unique:database_hosts,host',
210
            'port' => 'required|numeric|between:1,65535',
211
            'username' => 'required|string|max:32',
212
            'password' => 'required|string',
213
            'node_id' => 'sometimes|required|exists:nodes,id',
214
        ]);
215
216
        if ($validator->fails()) {
217
            throw new DisplayValidationException(json_encode($validator->errors()));
218
        }
219
220
        return DB::transaction(function () use ($data) {
221
            $host = new DatabaseHost;
222
            $host->password = Crypt::encrypt($data['password']);
223
224
            $host->fill([
225
                'name' => $data['name'],
226
                'host' => $data['host'],
227
                'port' => $data['port'],
228
                'username' => $data['username'],
229
                'max_databases' => null,
230
                'node_id' => (isset($data['node_id'])) ? $data['node_id'] : null,
231
            ])->save();
232
233
            // Allows us to check that we can connect to things.
234
            $host->setDynamicConnection();
235
            DB::connection('dynamic')->select('SELECT 1 FROM dual');
236
237
            return $host;
238
        });
239
    }
240
241
    /**
242
     * Updates a Database Host on the system.
243
     *
244
     * @param  int    $id
245
     * @param  array  $data
246
     * @return \Pterodactyl\Models\DatabaseHost
247
     *
248
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
249
     */
250
    public function update($id, array $data)
251
    {
252
        $host = DatabaseHost::findOrFail($id);
253
254
        if (isset($data['host'])) {
255
            $data['host'] = gethostbyname($data['host']);
256
        }
257
258
        $validator = Validator::make($data, [
259
            'name' => 'sometimes|required|string|max:255',
260
            'host' => 'sometimes|required|ip|unique:database_hosts,host,' . $host->id,
261
            'port' => 'sometimes|required|numeric|between:1,65535',
262
            'username' => 'sometimes|required|string|max:32',
263
            'password' => 'sometimes|required|string',
264
            'node_id' => 'sometimes|required|exists:nodes,id',
265
        ]);
266
267
        if ($validator->fails()) {
268
            throw new DisplayValidationException(json_encode($validator->errors()));
269
        }
270
271
        return DB::transaction(function () use ($data, $host) {
272
            if (isset($data['password'])) {
273
                $host->password = Crypt::encrypt($data['password']);
274
            }
275
            $host->fill($data)->save();
276
277
            // Check that we can still connect with these details.
278
            $host->setDynamicConnection();
279
            DB::connection('dynamic')->select('SELECT 1 FROM dual');
280
281
            return $host;
282
        });
283
    }
284
}
285