Logic cleanup after a bit of dust collection

This commit is contained in:
Dane Everitt 2022-02-13 14:15:18 -05:00
parent 8971e78ab5
commit 969d40d6c1
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
6 changed files with 74 additions and 43 deletions

View File

@ -61,6 +61,7 @@ class SecurityKeyController extends ClientApiController
$tokenId = Str::random(64); $tokenId = Str::random(64);
$credentials = $this->createPublicKeyCredentials->handle($request->user()); $credentials = $this->createPublicKeyCredentials->handle($request->user());
// TODO: session
$this->cache->put( $this->cache->put(
"register-security-key:$tokenId", "register-security-key:$tokenId",
serialize($credentials), serialize($credentials),

View File

@ -7,27 +7,27 @@ use Illuminate\Support\Str;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\View\View; use Pterodactyl\Models\SecurityKey;
use LaravelWebauthn\Facades\Webauthn; use Illuminate\Support\Facades\View;
use Illuminate\Contracts\View\Factory as ViewFactory; use Illuminate\Contracts\View\View as ViewContract;
use Pterodactyl\Services\Users\SecurityKeys\GeneratePublicKeyCredentialsRequestService;
class LoginController extends AbstractLoginController class LoginController extends AbstractLoginController
{ {
private const SESSION_PUBLICKEY_REQUEST = 'webauthn.publicKeyRequest';
private const METHOD_TOTP = 'totp'; private const METHOD_TOTP = 'totp';
private const METHOD_WEBAUTHN = 'webauthn'; private const METHOD_WEBAUTHN = 'webauthn';
private const SESSION_PUBLICKEY_REQUEST = 'webauthn.publicKeyRequest';
private ViewFactory $view; protected GeneratePublicKeyCredentialsRequestService $service;
/** /**
* LoginController constructor. * @param \Pterodactyl\Services\Users\SecurityKeys\GeneratePublicKeyCredentialsRequestService $service
*/ */
public function __construct(ViewFactory $view) public function __construct(GeneratePublicKeyCredentialsRequestService $service)
{ {
parent::__construct(); parent::__construct();
$this->view = $view; $this->service = $service;
} }
/** /**
@ -35,9 +35,9 @@ class LoginController extends AbstractLoginController
* base authentication view component. React will take over at this point and * base authentication view component. React will take over at this point and
* turn the login area into an SPA. * turn the login area into an SPA.
*/ */
public function index(): View public function index(): ViewContract
{ {
return $this->view->make('templates/auth.core'); return View::make('templates/auth.core');
} }
/** /**
@ -75,23 +75,11 @@ class LoginController extends AbstractLoginController
return; return;
} }
$useTotp = $user->use_totp; if (!$user->use_totp && empty($user->security_keys_count)) {
$webauthnKeys = $user->webauthnKeys()->get();
if (!$useTotp && count($webauthnKeys) < 1) {
return $this->sendLoginResponse($user, $request); return $this->sendLoginResponse($user, $request);
} }
$methods = [];
if ($useTotp) {
$methods[] = self::METHOD_TOTP;
}
if (count($webauthnKeys) > 0) {
$methods[] = self::METHOD_WEBAUTHN;
}
$token = Str::random(64); $token = Str::random(64);
$request->session()->put('auth_confirmation_token', [ $request->session()->put('auth_confirmation_token', [
'user_id' => $user->id, 'user_id' => $user->id,
'token_value' => $token, 'token_value' => $token,
@ -100,17 +88,21 @@ class LoginController extends AbstractLoginController
$response = [ $response = [
'complete' => false, 'complete' => false,
'methods' => $methods, 'methods' => array_filter([
$user->use_totp ? self::METHOD_TOTP : null,
$user->security_keys_count > 0 ? self::METHOD_WEBAUTHN : null,
]),
'confirmation_token' => $token, 'confirmation_token' => $token,
]; ];
if (count($webauthnKeys) > 0) { if ($user->security_keys_count > 0) {
$publicKey = Webauthn::getAuthenticateData($user); // $key = $this->service->handle($user);
$request->session()->put(self::SESSION_PUBLICKEY_REQUEST, $publicKey); //
// $request->session()->put(self::SESSION_PUBLICKEY_REQUEST, $publicKey);
$response['webauthn'] = [ //
'public_key' => $publicKey, // $response['webauthn'] = [
]; // 'public_key' => $publicKey,
// ];
} }
return new JsonResponse($response); return new JsonResponse($response);

View File

@ -2,7 +2,6 @@
namespace Pterodactyl\Models; namespace Pterodactyl\Models;
use Stringable;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface; use Ramsey\Uuid\UuidInterface;
use Webauthn\TrustPath\TrustPath; use Webauthn\TrustPath\TrustPath;
@ -82,12 +81,7 @@ class SecurityKey extends Model
return null; return null;
} }
public function user(): BelongsTo public function getPublicKeyCredentialDescriptor(): PublicKeyCredentialDescriptor
{
return $this->belongsTo(User::class);
}
public function getPublicKeyCredentialsDescriptorAttribute(): PublicKeyCredentialDescriptor
{ {
return new PublicKeyCredentialDescriptor( return new PublicKeyCredentialDescriptor(
$this->type, $this->type,
@ -96,7 +90,7 @@ class SecurityKey extends Model
); );
} }
public function getPublicKeyCredentialSourceAttribute(): PublicKeyCredentialSource public function getPublicKeyCredentialSource(): PublicKeyCredentialSource
{ {
return new PublicKeyCredentialSource( return new PublicKeyCredentialSource(
$this->public_key_id, $this->public_key_id,
@ -110,4 +104,9 @@ class SecurityKey extends Model
$this->counter $this->counter
); );
} }
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
} }

View File

@ -29,7 +29,7 @@ class PublicKeyCredentialSourceRepository implements PublicKeyRepositoryInterfac
->where('public_key_id', $id) ->where('public_key_id', $id)
->first(); ->first();
return $key ? $key->public_key_credential_source : null; return optional($key)->getPublicKeyCredentialSource();
} }
/** /**
@ -43,7 +43,7 @@ class PublicKeyCredentialSourceRepository implements PublicKeyRepositoryInterfac
->get(); ->get();
return $results->map(function (SecurityKey $key) { return $results->map(function (SecurityKey $key) {
return $key->public_key_credential_source; return $key->getPublicKeyCredentialSource();
})->values()->toArray(); })->values()->toArray();
} }

View File

@ -22,7 +22,7 @@ class CreatePublicKeyCredentialsService
$entity = new PublicKeyCredentialUserEntity($user->username, $user->uuid, $user->email, null); $entity = new PublicKeyCredentialUserEntity($user->username, $user->uuid, $user->email, null);
$excluded = $user->securityKeys->map(function (SecurityKey $key) { $excluded = $user->securityKeys->map(function (SecurityKey $key) {
return $key->public_key_credentials_descriptor; return $key->getPublicKeyCredentialDescriptor();
})->values()->toArray(); })->values()->toArray();
$server = $this->webauthnServerRepository->getServer($user); $server = $this->webauthnServerRepository->getServer($user);

View File

@ -0,0 +1,39 @@
<?php
namespace Pterodactyl\Services\Users\SecurityKeys;
use Pterodactyl\Models\User;
use Pterodactyl\Models\SecurityKey;
use Webauthn\PublicKeyCredentialRequestOptions;
use Pterodactyl\Repositories\SecurityKeys\WebauthnServerRepository;
class GeneratePublicKeyCredentialsRequestService
{
protected WebauthnServerRepository $serverRepository;
/**
* @param \Pterodactyl\Repositories\SecurityKeys\WebauthnServerRepository $serverRepository
*/
public function __construct(WebauthnServerRepository $serverRepository)
{
$this->serverRepository = $serverRepository;
}
/**
* @param \Pterodactyl\Models\User $user
* @return \Webauthn\PublicKeyCredentialRequestOptions
*/
public function handle(User $user): PublicKeyCredentialRequestOptions
{
$credentials = $user->securityKeys->map(function (SecurityKey $key) {
return $key->getPublicKeyCredentialDescriptor();
});
$response = $this->serverRepository->getServer($user)
->generatePublicKeyCredentialRequestOptions(
PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_PREFERRED, $credentials
);
return $response->setTimeout(300);
}
}