Skip to content

Commit 11d96c4

Browse files
committed
Merge branch 'feature/vuejs-serverlist' into feature/vue-serverview
2 parents 378a185 + a1444b0 commit 11d96c4

File tree

7 files changed

+140
-65
lines changed

7 files changed

+140
-65
lines changed

app/Http/Controllers/Auth/AbstractLoginController.php

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace Pterodactyl\Http\Controllers\Auth;
44

5+
use Cake\Chronos\Chronos;
6+
use Lcobucci\JWT\Builder;
57
use Illuminate\Http\Request;
8+
use Pterodactyl\Models\User;
69
use Illuminate\Auth\AuthManager;
710
use Illuminate\Http\JsonResponse;
811
use PragmaRX\Google2FA\Google2FA;
@@ -12,18 +15,24 @@
1215
use Illuminate\Contracts\Auth\Authenticatable;
1316
use Illuminate\Contracts\Encryption\Encrypter;
1417
use Illuminate\Foundation\Auth\AuthenticatesUsers;
18+
use Pterodactyl\Traits\Helpers\ProvidesJWTServices;
1519
use Illuminate\Contracts\Cache\Repository as CacheRepository;
1620
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
1721

1822
abstract class AbstractLoginController extends Controller
1923
{
20-
use AuthenticatesUsers;
24+
use AuthenticatesUsers, ProvidesJWTServices;
2125

2226
/**
2327
* @var \Illuminate\Auth\AuthManager
2428
*/
2529
protected $auth;
2630

31+
/**
32+
* @var \Lcobucci\JWT\Builder
33+
*/
34+
protected $builder;
35+
2736
/**
2837
* @var \Illuminate\Contracts\Cache\Repository
2938
*/
@@ -69,19 +78,22 @@ abstract class AbstractLoginController extends Controller
6978
* LoginController constructor.
7079
*
7180
* @param \Illuminate\Auth\AuthManager $auth
81+
* @param \Lcobucci\JWT\Builder $builder
7282
* @param \Illuminate\Contracts\Cache\Repository $cache
7383
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
7484
* @param \PragmaRX\Google2FA\Google2FA $google2FA
7585
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
7686
*/
7787
public function __construct(
7888
AuthManager $auth,
89+
Builder $builder,
7990
CacheRepository $cache,
8091
Encrypter $encrypter,
8192
Google2FA $google2FA,
8293
UserRepositoryInterface $repository
8394
) {
8495
$this->auth = $auth;
96+
$this->builder = $builder;
8597
$this->cache = $cache;
8698
$this->encrypter = $encrypter;
8799
$this->google2FA = $google2FA;
@@ -116,19 +128,34 @@ protected function sendFailedLoginResponse(Request $request, Authenticatable $us
116128
/**
117129
* Send the response after the user was authenticated.
118130
*
131+
* @param \Pterodactyl\Models\User $user
119132
* @param \Illuminate\Http\Request $request
120133
* @return \Illuminate\Http\JsonResponse
121134
*/
122-
protected function sendLoginResponse(Request $request): JsonResponse
135+
protected function sendLoginResponse(User $user, Request $request): JsonResponse
123136
{
124137
$request->session()->regenerate();
125-
126138
$this->clearLoginAttempts($request);
127139

128-
return $this->authenticated($request, $this->guard()->user())
129-
?: response()->json([
130-
'intended' => $this->redirectPath(),
131-
]);
140+
$token = $this->builder->setIssuer(config('app.url'))
141+
->setAudience(config('app.url'))
142+
->setId(str_random(12), true)
143+
->setIssuedAt(Chronos::now()->getTimestamp())
144+
->setNotBefore(Chronos::now()->getTimestamp())
145+
->setExpiration(Chronos::now()->addSeconds(config('session.lifetime'))->getTimestamp())
146+
->set('user', $user->only([
147+
'id', 'uuid', 'username', 'email', 'name_first', 'name_last', 'language', 'root_admin',
148+
]))
149+
->sign($this->getJWTSigner(), $this->getJWTSigningKey())
150+
->getToken();
151+
152+
$this->auth->guard()->login($user, true);
153+
154+
return response()->json([
155+
'complete' => true,
156+
'intended' => $this->redirectPath(),
157+
'token' => $token->__toString(),
158+
]);
132159
}
133160

134161
/**

app/Http/Controllers/Auth/LoginCheckpointController.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ public function __invoke(LoginCheckpointRequest $request): JsonResponse
3939
return $this->sendFailedLoginResponse($request, $user);
4040
}
4141

42-
$this->auth->guard()->login($user, true);
43-
44-
return $this->sendLoginResponse($request);
42+
return $this->sendLoginResponse($user, $request);
4543
}
4644
}

app/Http/Controllers/Auth/LoginController.php

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22

33
namespace Pterodactyl\Http\Controllers\Auth;
44

5-
use Lcobucci\JWT\Builder;
65
use Illuminate\Http\Request;
76
use Illuminate\Http\JsonResponse;
87
use Illuminate\Contracts\View\View;
9-
use Lcobucci\JWT\Signer\Hmac\Sha256;
108
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
119

1210
class LoginController extends AbstractLoginController
@@ -65,26 +63,12 @@ public function login(Request $request): JsonResponse
6563
'request_ip' => $request->ip(),
6664
], 5);
6765

68-
return response()->json(['complete' => false, 'login_token' => $token]);
66+
return response()->json([
67+
'complete' => false,
68+
'login_token' => $token,
69+
]);
6970
}
7071

71-
$signer = new Sha256();
72-
$token = (new Builder)->setIssuer('http://pterodactyl.local')
73-
->setAudience('http://pterodactyl.local')
74-
->setId(str_random(12), true)
75-
->setIssuedAt(time())
76-
->setNotBefore(time())
77-
->setExpiration(time() + 3600)
78-
->set('uid', $user->id)
79-
->sign($signer, env('APP_JWT_KEY'))
80-
->getToken();
81-
82-
$this->auth->guard()->login($user, true);
83-
84-
return response()->json([
85-
'complete' => true,
86-
'intended' => $this->redirectPath(),
87-
'token' => $token->__toString(),
88-
]);
72+
return $this->sendLoginResponse($user, $request);
8973
}
9074
}

app/Http/Middleware/Api/AuthenticateKey.php

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@
99
use Pterodactyl\Models\ApiKey;
1010
use Illuminate\Auth\AuthManager;
1111
use Illuminate\Contracts\Encryption\Encrypter;
12+
use Pterodactyl\Traits\Helpers\ProvidesJWTServices;
1213
use Symfony\Component\HttpKernel\Exception\HttpException;
1314
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
1415
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface;
1516
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
1617

1718
class AuthenticateKey
1819
{
20+
use ProvidesJWTServices;
21+
1922
/**
2023
* @var \Illuminate\Auth\AuthManager
2124
*/
@@ -65,24 +68,55 @@ public function handle(Request $request, Closure $next, int $keyType)
6568

6669
$raw = $request->bearerToken();
6770

68-
// This is an internal JWT, treat it differently to get the correct user
69-
// before passing it along.
71+
// This is an internal JWT, treat it differently to get the correct user before passing it along.
7072
if (strlen($raw) > ApiKey::IDENTIFIER_LENGTH + ApiKey::KEY_LENGTH) {
71-
$token = (new Parser)->parse($raw);
73+
$model = $this->authenticateJWT($raw);
74+
} else {
75+
$model = $this->authenticateApiKey($raw, $keyType);
76+
}
7277

73-
$model = (new ApiKey)->fill([
74-
'user_id' => $token->getClaim('uid'),
75-
'key_type' => ApiKey::TYPE_ACCOUNT,
76-
]);
78+
$this->auth->guard()->loginUsingId($model->user_id);
79+
$request->attributes->set('api_key', $model);
7780

78-
$this->auth->guard()->loginUsingId($token->getClaim('uid'));
79-
$request->attributes->set('api_key', $model);
81+
return $next($request);
82+
}
8083

81-
return $next($request);
84+
/**
85+
* Authenticate an API request using a JWT rather than an API key.
86+
*
87+
* @param string $token
88+
* @return \Pterodactyl\Models\ApiKey
89+
*/
90+
protected function authenticateJWT(string $token): ApiKey
91+
{
92+
$token = (new Parser)->parse($token);
93+
94+
// If the key cannot be verified throw an exception to indicate that a bad
95+
// authorization header was provided.
96+
if (! $token->verify($this->getJWTSigner(), $this->getJWTSigningKey())) {
97+
throw new HttpException(401, null, null, ['WWW-Authenticate' => 'Bearer']);
8298
}
8399

84-
$identifier = substr($raw, 0, ApiKey::IDENTIFIER_LENGTH);
85-
$token = substr($raw, ApiKey::IDENTIFIER_LENGTH);
100+
return (new ApiKey)->forceFill([
101+
'user_id' => object_get($token->getClaim('user'), 'id', 0),
102+
'key_type' => ApiKey::TYPE_ACCOUNT,
103+
]);
104+
}
105+
106+
/**
107+
* Authenticate an API key.
108+
*
109+
* @param string $key
110+
* @param int $keyType
111+
* @return \Pterodactyl\Models\ApiKey
112+
*
113+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
114+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
115+
*/
116+
protected function authenticateApiKey(string $key, int $keyType): ApiKey
117+
{
118+
$identifier = substr($key, 0, ApiKey::IDENTIFIER_LENGTH);
119+
$token = substr($key, ApiKey::IDENTIFIER_LENGTH);
86120

87121
try {
88122
$model = $this->repository->findFirstWhere([
@@ -97,10 +131,8 @@ public function handle(Request $request, Closure $next, int $keyType)
97131
throw new AccessDeniedHttpException;
98132
}
99133

100-
$this->auth->guard()->loginUsingId($model->user_id);
101-
$request->attributes->set('api_key', $model);
102134
$this->repository->withoutFreshModel()->update($model->id, ['last_used_at' => Chronos::now()]);
103135

104-
return $next($request);
136+
return $model;
105137
}
106138
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Pterodactyl\Traits\Helpers;
4+
5+
use Lcobucci\JWT\Signer;
6+
use Illuminate\Support\Str;
7+
8+
trait ProvidesJWTServices
9+
{
10+
/**
11+
* Get the signing key to use when creating JWTs.
12+
*
13+
* @return string
14+
*/
15+
public function getJWTSigningKey(): string
16+
{
17+
$key = config()->get('jwt.key', '');
18+
if (Str::startsWith($key, 'base64:')) {
19+
$key = base64_decode(substr($key, 7));
20+
}
21+
22+
return $key;
23+
}
24+
25+
/**
26+
* Provide the signing algo to use for JWT.
27+
*
28+
* @return \Lcobucci\JWT\Signer
29+
*/
30+
public function getJWTSigner(): Signer
31+
{
32+
$class = config()->get('jwt.signer');
33+
34+
return new $class;
35+
}
36+
}

config/jwt.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
return [
4+
/*
5+
|--------------------------------------------------------------------------
6+
| JWT Signing Key
7+
|--------------------------------------------------------------------------
8+
|
9+
| This key is used for the verification of JSON Web Tokens in flight and
10+
| should be different than the application encryption key. This key should
11+
| be kept private at all times.
12+
|
13+
*/
14+
'key' => env('APP_JWT_KEY'),
15+
16+
'signer' => \Lcobucci\JWT\Signer\Hmac\Sha256::class,
17+
];

resources/assets/scripts/models/allocation.js

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)