Skip to content

Commit f0e4764

Browse files
Add Max Concurrent Connections for database users
Closes pterodactyl#1849 Allows database users to be limited to a number of concurrent connections to prevent one user from connecting hundreds of time and bottlenecking the MySQL server.
1 parent 2c3a922 commit f0e4764

File tree

10 files changed

+56
-6
lines changed

10 files changed

+56
-6
lines changed

app/Contracts/Repository/DatabaseRepositoryInterface.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ public function createDatabase(string $database): bool;
6868
* @param string $username
6969
* @param string $remote
7070
* @param string $password
71+
* @param string $max_connections
7172
* @return bool
7273
*/
73-
public function createUser(string $username, string $remote, string $password): bool;
74+
public function createUser(string $username, string $remote, string $password, string $max_connections): bool;
7475

7576
/**
7677
* Give a specific user access to a given database.

app/Models/Database.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class Database extends Model
3030
* @var array
3131
*/
3232
protected $fillable = [
33-
'server_id', 'database_host_id', 'database', 'username', 'password', 'remote',
33+
'server_id', 'database_host_id', 'database', 'username', 'password', 'remote', 'max_connections',
3434
];
3535

3636
/**
@@ -51,6 +51,7 @@ class Database extends Model
5151
'database_host_id' => 'required|exists:database_hosts,id',
5252
'database' => 'required|string|alpha_dash|between:3,100',
5353
'username' => 'string|alpha_dash|between:3,100',
54+
'max_connections' => 'string',
5455
'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/',
5556
'password' => 'string',
5657
];

app/Repositories/Eloquent/DatabaseRepository.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,12 @@ public function createDatabase(string $database): bool
135135
* @param string $username
136136
* @param string $remote
137137
* @param string $password
138+
* @param string $max_connections
138139
* @return bool
139140
*/
140-
public function createUser(string $username, string $remote, string $password): bool
141+
public function createUser(string $username, string $remote, string $password, string $max_connections): bool
141142
{
142-
return $this->run(sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', $username, $remote, $password));
143+
return $this->run(sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\' WITH MAX_USER_CONNECTIONS %s', $username, $remote, $password, $max_connections));
143144
}
144145

145146
/**

app/Services/Databases/DatabaseManagementService.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ public function create($server, array $data)
8484
$this->repository->createUser(
8585
$database->username,
8686
$database->remote,
87-
$this->encrypter->decrypt($database->password)
87+
$this->encrypter->decrypt($database->password),
88+
$database->max_connections
8889
);
8990
$this->repository->assignUserToDatabase(
9091
$database->database,

app/Services/Databases/DatabasePasswordService.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public function handle(Database $database): string
7171
]);
7272

7373
$this->repository->dropUser($database->username, $database->remote);
74-
$this->repository->createUser($database->username, $database->remote, $password);
74+
$this->repository->createUser($database->username, $database->remote, $password, $database->max_connections);
7575
$this->repository->assignUserToDatabase($database->database, $database->username, $database->remote);
7676
$this->repository->flush();
7777
});

app/Transformers/Api/Application/ServerDatabaseTransformer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public function transform(Database $model): array
5656
'database' => $model->database,
5757
'username' => $model->username,
5858
'remote' => $model->remote,
59+
'max_connections' => $model->max_connections,
5960
'created_at' => Chronos::createFromFormat(Chronos::DEFAULT_TO_STRING_FORMAT, $model->created_at)
6061
->setTimezone(config('app.timezone'))
6162
->toIso8601String(),
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class AddMaxConnectionsColumnToDatabasesTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::table('databases', function (Blueprint $table) {
17+
$table->integer('max_connections')->nullable(false)->default(0)->after('password');
18+
});
19+
}
20+
21+
/**
22+
* Reverse the migrations.
23+
*
24+
* @return void
25+
*/
26+
public function down()
27+
{
28+
Schema::table('databases', function (Blueprint $table) {
29+
$table->dropColumn('max_connections');
30+
});
31+
}
32+
}

resources/scripts/components/server/databases/DatabaseRow.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ export default ({ database, className }: Props) => {
151151
<p className={'text-sm'}>{database.username}</p>
152152
<p className={'mt-1 text-2xs text-neutral-500 uppercase select-none'}>Username</p>
153153
</div>
154+
<div className={'ml-8 text-center'}>
155+
<p className={'text-sm'}>{database.max_connections}</p>
156+
<p className={'mt-1 text-2xs text-neutral-500 uppercase select-none'}>Max Connections</p>
157+
</div>
154158
<div className={'ml-8'}>
155159
<button className={'btn btn-sm btn-secondary mr-2'} onClick={() => setConnectionVisible(true)}>
156160
<FontAwesomeIcon icon={faEye} fixedWidth={true}/>

resources/views/admin/databases/view.blade.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
<th>Database Name</th>
100100
<th>Username</th>
101101
<th>Connections From</th>
102+
<th>Max Connections</th>
102103
<th></th>
103104
</tr>
104105
@foreach($databases as $database)
@@ -107,6 +108,7 @@
107108
<td class="middle">{{ $database->database }}</td>
108109
<td class="middle">{{ $database->username }}</td>
109110
<td class="middle">{{ $database->remote }}</td>
111+
<td class="middle">{{ $database->max_connections }}</td>
110112
<td class="text-center">
111113
<a href="{{ route('admin.servers.view.database', $database->getRelation('server')->id) }}">
112114
<button class="btn btn-xs btn-primary">Manage</button>

resources/views/admin/servers/view/database.blade.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<th>Username</th>
3838
<th>Connections From</th>
3939
<th>Host</th>
40+
<th>Max Conenctions</th>
4041
<th></th>
4142
</tr>
4243
@foreach($server->databases as $database)
@@ -45,6 +46,7 @@
4546
<td>{{ $database->username }}</td>
4647
<td>{{ $database->remote }}</td>
4748
<td><code>{{ $database->host->host }}:{{ $database->host->port }}</code></td>
49+
<td>{{ $database->max_connections }}</td>
4850
<td class="text-center">
4951
<button data-action="reset-password" data-id="{{ $database->id }}" class="btn btn-xs btn-primary"><i class="fa fa-refresh"></i></button>
5052
<button data-action="remove" data-id="{{ $database->id }}" class="btn btn-xs btn-danger"><i class="fa fa-trash"></i></button>
@@ -83,6 +85,11 @@
8385
<input id="pRemote" type="text" name="remote" class="form-control" value="%" />
8486
<p class="text-muted small">This should reflect the IP address that connections are allowed from. Uses standard MySQL notation. If unsure leave as <code>%</code>.</p>
8587
</div>
88+
<div class="form-group">
89+
<label for="pmax_connections" class="control-label">Max Concurrent Connections</label>
90+
<input id="pmax_connections" type="text" name="max_connections" class="form-control" value="150" />
91+
<p class="text-muted small">This should reflect the max number of concurrent connections from this user to the database. Use <code>0</code> for unlimited</p>
92+
</div>
8693
</div>
8794
<div class="box-footer">
8895
{!! csrf_field() !!}

0 commit comments

Comments
 (0)