Skip to content

Commit 5664025

Browse files
committed
Merge branch 'release/v0.7.14' into feature/react
2 parents 2a626a3 + c38f78d commit 5664025

File tree

16 files changed

+178
-59
lines changed

16 files changed

+178
-59
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@ This file is a running track of new features and fixes to each version of the pa
33

44
This project follows [Semantic Versioning](http://semver.org) guidelines.
55

6+
## v0.7.14 (Derelict Dermodactylus)
7+
### Fixed
8+
* **[SECURITY]** Fixes an XSS vulnerability when performing certain actions in the file manager.
9+
* **[SECURITY]** Attempting to login as a user who has 2FA enabled will no longer request the 2FA token before validating
10+
that their password is correct. This closes a user existence leak that would expose that an account exists if
11+
it had 2FA enabled.
12+
13+
### Changed
14+
* Support for setting a node to listen on ports lower than 1024.
15+
* QR code URLs are now generated without the use of an external library to reduce the dependency tree.
16+
* Regenerated database passwords now respect the same settings that were used when initially created.
17+
* Cleaned up 2FA QR code generation to use a more up-to-date library and API.
18+
* Console charts now properly start at 0 and scale based on server configuration. No more crazy spikes that
19+
are due to a change of one unit.
20+
621
## v0.7.13 (Derelict Dermodactylus)
722
### Fixed
823
* Fixes a bug with the location update API endpoint throwing an error due to an unexected response value.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![Logo Image](https://cdn.pterodactyl.io/logos/Banner%20Logo%20Black@2x.png)](https://pterodactyl.io)
1+
[![Logo Image](https://cdn.pterodactyl.io/logos/new/pterodactyl_logo.png)](https://pterodactyl.io)
22

33
[![Build status](https://img.shields.io/travis/pterodactyl/panel/develop.svg?style=flat-square)](https://travis-ci.org/pterodactyl/panel)
44
[![StyleCI](https://styleci.io/repos/47508644/shield?branch=develop)](https://styleci.io/repos/47508644)

app/Http/Controllers/Auth/LoginController.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,71 @@ public function login(Request $request): JsonResponse
5454
return $this->sendFailedLoginResponse($request, $user);
5555
}
5656

57+
if ($user->use_totp) {
58+
$token = str_random(64);
59+
$this->cache->put($token, ['user_id' => $user->id, 'valid_credentials' => true], 5);
60+
61+
return redirect()->route('auth.totp')->with('authentication_token', $token);
62+
}
63+
64+
$this->auth->guard()->login($user, true);
65+
66+
return $this->sendLoginResponse($user, $request);
67+
}
68+
69+
/**
70+
* Handle a TOTP implementation page.
71+
*
72+
* @param \Illuminate\Http\Request $request
73+
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
74+
*/
75+
public function totp(Request $request)
76+
{
77+
$token = $request->session()->get('authentication_token');
78+
if (is_null($token) || $this->auth->guard()->user()) {
79+
return redirect()->route('auth.login');
80+
}
81+
82+
return view('auth.totp', ['verify_key' => $token]);
83+
}
84+
85+
/**
86+
* Handle a login where the user is required to provide a TOTP authentication
87+
* token.
88+
*
89+
* @param \Illuminate\Http\Request $request
90+
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response
91+
*
92+
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
93+
* @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
94+
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
95+
* @throws \Pterodactyl\Exceptions\DisplayException
96+
*/
97+
public function loginUsingTotp(Request $request)
98+
{
99+
if (is_null($request->input('verify_token'))) {
100+
return $this->sendFailedLoginResponse($request);
101+
}
102+
103+
try {
104+
$cache = $this->cache->pull($request->input('verify_token'), []);
105+
$user = $this->repository->find(array_get($cache, 'user_id', 0));
106+
} catch (RecordNotFoundException $exception) {
107+
return $this->sendFailedLoginResponse($request);
108+
}
109+
110+
if (is_null($request->input('2fa_token'))) {
111+
return $this->sendFailedLoginResponse($request, $user);
112+
}
113+
114+
if (! $this->google2FA->verifyKey(
115+
$this->encrypter->decrypt($user->totp_secret),
116+
$request->input('2fa_token'),
117+
$this->config->get('pterodactyl.auth.2fa.window')
118+
)) {
119+
return $this->sendFailedLoginResponse($request, $user);
120+
}
121+
57122
// If the user is using 2FA we do not actually log them in at this step, we return
58123
// a one-time token to link the 2FA credentials to this account via the UI.
59124
if ($user->use_totp) {

app/Http/Controllers/Base/SecurityController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ public function index(Request $request): JsonResponse
8383

8484
return JsonResponse::create([
8585
'enabled' => false,
86-
'qr_image' => $response->get('image'),
87-
'secret' => $response->get('secret'),
86+
'qr_image' => $response,
87+
'secret' => '',
8888
]);
8989
}
9090

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
<?php
2-
/**
3-
* Pterodactyl - Panel
4-
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5-
*
6-
* This software is licensed under the terms of the MIT license.
7-
* https://opensource.org/licenses/MIT
8-
*/
92

103
namespace Pterodactyl\Services\Users;
114

5+
use Exception;
6+
use RuntimeException;
127
use Pterodactyl\Models\User;
13-
use Illuminate\Support\Collection;
14-
use PragmaRX\Google2FAQRCode\Google2FA;
158
use Illuminate\Contracts\Encryption\Encrypter;
169
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
1710
use Illuminate\Contracts\Config\Repository as ConfigRepository;
1811

1912
class TwoFactorSetupService
2013
{
14+
const VALID_BASE32_CHARACTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
15+
2116
/**
2217
* @var \Illuminate\Contracts\Config\Repository
2318
*/
@@ -28,11 +23,6 @@ class TwoFactorSetupService
2823
*/
2924
private $encrypter;
3025

31-
/**
32-
* @var PragmaRX\Google2FAQRCode\Google2FA
33-
*/
34-
private $google2FA;
35-
3626
/**
3727
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
3828
*/
@@ -43,43 +33,51 @@ class TwoFactorSetupService
4333
*
4434
* @param \Illuminate\Contracts\Config\Repository $config
4535
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
46-
* @param PragmaRX\Google2FAQRCode\Google2FA $google2FA
4736
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
4837
*/
4938
public function __construct(
5039
ConfigRepository $config,
5140
Encrypter $encrypter,
52-
Google2FA $google2FA,
5341
UserRepositoryInterface $repository
5442
) {
5543
$this->config = $config;
5644
$this->encrypter = $encrypter;
57-
$this->google2FA = $google2FA;
5845
$this->repository = $repository;
5946
}
6047

6148
/**
6249
* Generate a 2FA token and store it in the database before returning the
63-
* QR code image.
50+
* QR code URL. This URL will need to be attached to a QR generating service in
51+
* order to function.
6452
*
6553
* @param \Pterodactyl\Models\User $user
66-
* @return \Illuminate\Support\Collection
54+
* @return string
6755
*
6856
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
6957
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
7058
*/
71-
public function handle(User $user): Collection
59+
public function handle(User $user): string
7260
{
73-
$secret = $this->google2FA->generateSecretKey($this->config->get('pterodactyl.auth.2fa.bytes'));
74-
$image = $this->google2FA->getQRCodeInline($this->config->get('app.name'), $user->email, $secret);
61+
$secret = '';
62+
try {
63+
for ($i = 0; $i < $this->config->get('pterodactyl.auth.2fa.bytes', 16); $i++) {
64+
$secret .= substr(self::VALID_BASE32_CHARACTERS, random_int(0, 31), 1);
65+
}
66+
} catch (Exception $exception) {
67+
throw new RuntimeException($exception->getMessage(), 0, $exception);
68+
}
7569

7670
$this->repository->withoutFreshModel()->update($user->id, [
7771
'totp_secret' => $this->encrypter->encrypt($secret),
7872
]);
7973

80-
return new Collection([
81-
'image' => $image,
82-
'secret' => $secret,
83-
]);
74+
$company = $this->config->get('app.name');
75+
76+
return sprintf(
77+
'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s',
78+
rawurlencode($company),
79+
rawurlencode($user->email),
80+
rawurlencode($secret)
81+
);
8482
}
8583
}

composer.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
"matriphe/iso-639": "^1.2",
3131
"nesbot/carbon": "^1.22",
3232
"pragmarx/google2fa": "^5.0",
33-
"pragmarx/google2fa-qrcode": "^1.0.3",
3433
"predis/predis": "^1.1",
3534
"prologue/alerts": "^0.4",
3635
"ramsey/uuid": "^3.7",

config/app.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
| change this value if you are not maintaining your own internal versions.
1010
*/
1111

12-
'version' => 'canary',
12+
'version' => '0.7.14',
1313

1414
/*
1515
|--------------------------------------------------------------------------

config/pterodactyl.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?php
22

3-
43
return [
54
/*
65
|--------------------------------------------------------------------------

public/themes/pterodactyl/js/frontend/console.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,31 @@ $(document).ready(function () {
255255

256256
TimeLabels.push($.format.date(new Date(), 'HH:mm:ss'));
257257

258+
259+
// memory.cmax is the maximum given by the container
260+
// memory.amax is given by the json config
261+
// use the maximum of both
262+
// with no limit memory.cmax will always be higher
263+
// but with limit memory.amax is sometimes still smaller than memory.total
264+
MemoryChart.config.options.scales.yAxes[0].ticks.max = Math.max(proc.data.memory.cmax, proc.data.memory.amax) / (1000 * 1000);
265+
266+
if (Pterodactyl.server.cpu > 0) {
267+
// if there is a cpu limit defined use 100% as maximum
268+
CPUChart.config.options.scales.yAxes[0].ticks.max = 100;
269+
} else {
270+
// if there is no cpu limit defined use linux percentage
271+
// and find maximum in all values
272+
var maxCpu = 1;
273+
for(var i = 0; i < CPUData.length; i++) {
274+
maxCpu = Math.max(maxCpu, parseFloat(CPUData[i]))
275+
}
276+
277+
maxCpu = Math.ceil(maxCpu / 100) * 100;
278+
CPUChart.config.options.scales.yAxes[0].ticks.max = maxCpu;
279+
}
280+
281+
282+
258283
CPUChart.update();
259284
MemoryChart.update();
260285
});
@@ -301,6 +326,13 @@ $(document).ready(function () {
301326
},
302327
animation: {
303328
duration: 1,
329+
},
330+
scales: {
331+
yAxes: [{
332+
ticks: {
333+
beginAtZero: true
334+
}
335+
}]
304336
}
305337
}
306338
});
@@ -346,6 +378,13 @@ $(document).ready(function () {
346378
},
347379
animation: {
348380
duration: 1,
381+
},
382+
scales: {
383+
yAxes: [{
384+
ticks: {
385+
beginAtZero: true
386+
}
387+
}]
349388
}
350389
}
351390
});

0 commit comments

Comments
 (0)