cleanup, switch to attributes
This commit is contained in:
parent
f631ac1946
commit
ba7ff571e5
9 changed files with 80 additions and 63 deletions
|
@ -63,7 +63,7 @@ class LoginCheckpointController extends AbstractLoginController
|
||||||
|
|
||||||
$decrypted = $this->encrypter->decrypt($user->totp_secret);
|
$decrypted = $this->encrypter->decrypt($user->totp_secret);
|
||||||
|
|
||||||
if ($this->google2FA->verifyKey($decrypted, (string) $request->input('authentication_code') ?? '', config('pterodactyl.auth.2fa.window'))) {
|
if ($this->google2FA->verifyKey($decrypted, $request->input('authentication_code') ?? '', config('pterodactyl.auth.2fa.window'))) {
|
||||||
return $this->sendLoginResponse($user, $request);
|
return $this->sendLoginResponse($user, $request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Models;
|
namespace Pterodactyl\Models;
|
||||||
|
|
||||||
use Ramsey\Uuid\Uuid;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Ramsey\Uuid\UuidInterface;
|
use Symfony\Component\Uid\Uuid;
|
||||||
use Webauthn\TrustPath\TrustPath;
|
use Webauthn\TrustPath\TrustPath;
|
||||||
|
use Symfony\Component\Uid\NilUuid;
|
||||||
use Nyholm\Psr7\Factory\Psr17Factory;
|
use Nyholm\Psr7\Factory\Psr17Factory;
|
||||||
|
use Symfony\Component\Uid\AbstractUid;
|
||||||
use Webauthn\PublicKeyCredentialSource;
|
use Webauthn\PublicKeyCredentialSource;
|
||||||
use Webauthn\TrustPath\TrustPathLoader;
|
use Webauthn\TrustPath\TrustPathLoader;
|
||||||
use Webauthn\PublicKeyCredentialDescriptor;
|
use Webauthn\PublicKeyCredentialDescriptor;
|
||||||
|
@ -14,7 +15,27 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
|
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $uuid
|
||||||
|
* @property int $user_id
|
||||||
|
* @property string $name
|
||||||
|
* @property string $public_key_id
|
||||||
|
* @property string $public_key
|
||||||
|
* @property AbstractUid $aaguid
|
||||||
|
* @property string $type
|
||||||
|
* @property string[] $transports
|
||||||
|
* @property string $attestation_type
|
||||||
|
* @property \Webauthn\TrustPath\TrustPath $trust_path
|
||||||
|
* @property string $user_handle
|
||||||
|
* @property int $counter
|
||||||
|
* @property array<string, mixed>|null $other_ui
|
||||||
|
*
|
||||||
|
* @property \Carbon\CarbonImmutable $created_at
|
||||||
|
* @property \Carbon\CarbonImmutable $updated_at
|
||||||
|
*/
|
||||||
class SecurityKey extends Model
|
class SecurityKey extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
@ -33,57 +54,36 @@ class SecurityKey extends Model
|
||||||
'user_id',
|
'user_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getPublicKeyAttribute(string $value): string
|
public function publicKey(): Attribute
|
||||||
{
|
{
|
||||||
return base64_decode($value);
|
return new Attribute(
|
||||||
|
get: fn (string $value) => base64_decode($value),
|
||||||
|
set: fn (string $value) => base64_encode($value),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPublicKeyAttribute(string $value): void
|
public function publicKeyId(): Attribute
|
||||||
{
|
{
|
||||||
$this->attributes['public_key'] = base64_encode($value);
|
return new Attribute(
|
||||||
|
get: fn (string $value) => base64_decode($value),
|
||||||
|
set: fn (string $value) => base64_encode($value),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPublicKeyIdAttribute(string $value): string
|
public function aaguid(): Attribute
|
||||||
{
|
{
|
||||||
return base64_decode($value);
|
return Attribute::make(
|
||||||
|
get: fn (string|null $value): AbstractUid => is_null($value) ? new NilUuid() : Uuid::fromString($value),
|
||||||
|
set: fn (AbstractUid|null $value): string|null => (is_null($value) || $value instanceof NilUuid) ? null : $value->__toString(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPublicKeyIdAttribute(string $value): void
|
public function trustPath(): Attribute
|
||||||
{
|
{
|
||||||
$this->attributes['public_key_id'] = base64_encode($value);
|
return new Attribute(
|
||||||
}
|
get: fn (mixed $value) => is_null($value) ? null : TrustPathLoader::loadTrustPath(json_decode($value, true)),
|
||||||
|
set: fn (TrustPath|null $value) => json_encode($value),
|
||||||
public function getTrustPathAttribute(?string $value): ?TrustPath
|
);
|
||||||
{
|
|
||||||
if (is_null($value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TrustPathLoader::loadTrustPath(json_decode($value, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setTrustPathAttribute(?TrustPath $value): void
|
|
||||||
{
|
|
||||||
$this->attributes['trust_path'] = json_encode($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \Ramsey\Uuid\UuidInterface|string|null $value
|
|
||||||
*/
|
|
||||||
public function setAaguidAttribute($value): void
|
|
||||||
{
|
|
||||||
$value = $value instanceof UuidInterface ? $value->__toString() : $value;
|
|
||||||
|
|
||||||
$this->attributes['aaguid'] = (is_null($value) || $value === Uuid::NIL) ? null : $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAaguidAttribute(?string $value): ?UuidInterface
|
|
||||||
{
|
|
||||||
if (!is_null($value) && Uuid::isValid($value)) {
|
|
||||||
return Uuid::fromString($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPublicKeyCredentialDescriptor(): PublicKeyCredentialDescriptor
|
public function getPublicKeyCredentialDescriptor(): PublicKeyCredentialDescriptor
|
||||||
|
@ -99,7 +99,7 @@ class SecurityKey extends Model
|
||||||
$this->transports,
|
$this->transports,
|
||||||
$this->attestation_type,
|
$this->attestation_type,
|
||||||
$this->trust_path,
|
$this->trust_path,
|
||||||
$this->aaguid ?? Uuid::fromString(Uuid::NIL),
|
$this->aaguid,
|
||||||
$this->public_key,
|
$this->public_key,
|
||||||
$this->user_handle,
|
$this->user_handle,
|
||||||
$this->counter
|
$this->counter
|
||||||
|
|
|
@ -38,7 +38,9 @@ class DeployServerDatabaseService
|
||||||
throw new NoSuitableDatabaseHostException();
|
throw new NoSuitableDatabaseHostException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$databaseHostId = $hosts->random()->id;
|
/** @var \Pterodactyl\Models\DatabaseHost $databaseHost */
|
||||||
|
$databaseHost = $hosts->random();
|
||||||
|
$databaseHostId = $databaseHost->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->managementService->create($server, [
|
return $this->managementService->create($server, [
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
namespace Pterodactyl\Transformers\Api\Client;
|
namespace Pterodactyl\Transformers\Api\Client;
|
||||||
|
|
||||||
use Pterodactyl\Models\SecurityKey;
|
use Pterodactyl\Models\SecurityKey;
|
||||||
|
use Pterodactyl\Transformers\Api\Transformer;
|
||||||
|
|
||||||
class SecurityKeyTransformer extends BaseClientTransformer
|
class SecurityKeyTransformer extends Transformer
|
||||||
{
|
{
|
||||||
public function getResourceName(): string
|
public function getResourceName(): string
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import useSWR, { ConfigInterface } from 'swr';
|
import type { AxiosError } from 'axios';
|
||||||
import { useStoreState } from '@/state/hooks';
|
import type { SWRConfiguration } from 'swr';
|
||||||
import http, { FractalResponseList } from '@/api/http';
|
import useSWR from 'swr';
|
||||||
import { SecurityKey, Transformers } from '@definitions/user';
|
|
||||||
import { AxiosError } from 'axios';
|
import type { SecurityKey } from '@definitions/user';
|
||||||
|
import { Transformers } from '@definitions/user';
|
||||||
|
import { LoginResponse } from '@/api/auth/login';
|
||||||
|
import type { FractalResponseList } from '@/api/http';
|
||||||
|
import http from '@/api/http';
|
||||||
import { decodeBase64 } from '@/lib/base64';
|
import { decodeBase64 } from '@/lib/base64';
|
||||||
import { decodeBuffer, encodeBuffer } from '@/lib/buffer';
|
import { decodeBuffer, encodeBuffer } from '@/lib/buffer';
|
||||||
import { LoginResponse } from '@/api/auth/login';
|
|
||||||
import { useUserSWRKey } from '@/plugins/useSWRKey';
|
import { useUserSWRKey } from '@/plugins/useSWRKey';
|
||||||
|
|
||||||
function decodeSecurityKeyCredentials(credentials: PublicKeyCredentialDescriptor[]) {
|
function decodeSecurityKeyCredentials(credentials: PublicKeyCredentialDescriptor[]) {
|
||||||
|
@ -16,8 +19,7 @@ function decodeSecurityKeyCredentials(credentials: PublicKeyCredentialDescriptor
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function useSecurityKeys(config?: ConfigInterface<SecurityKey[], AxiosError>) {
|
function useSecurityKeys(config?: SWRConfiguration<SecurityKey[], AxiosError>) {
|
||||||
const uuid = useStoreState(state => state.user.data!.uuid);
|
|
||||||
const key = useUserSWRKey(['account', 'security-keys']);
|
const key = useUserSWRKey(['account', 'security-keys']);
|
||||||
|
|
||||||
return useSWR<SecurityKey[], AxiosError>(
|
return useSWR<SecurityKey[], AxiosError>(
|
||||||
|
@ -25,9 +27,9 @@ function useSecurityKeys(config?: ConfigInterface<SecurityKey[], AxiosError>) {
|
||||||
async (): Promise<SecurityKey[]> => {
|
async (): Promise<SecurityKey[]> => {
|
||||||
const { data } = await http.get('/api/client/account/security-keys');
|
const { data } = await http.get('/api/client/account/security-keys');
|
||||||
|
|
||||||
return (data as FractalResponseList).data.map((datum) => Transformers.toSecurityKey(datum.attributes));
|
return (data as FractalResponseList).data.map(datum => Transformers.toSecurityKey(datum.attributes));
|
||||||
},
|
},
|
||||||
{ revalidateOnMount: false, ...(config || {}) },
|
{ revalidateOnMount: false, ...(config ?? {}) },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +37,11 @@ async function deleteSecurityKey(uuid: string): Promise<void> {
|
||||||
await http.delete(`/api/client/account/security-keys/${uuid}`);
|
await http.delete(`/api/client/account/security-keys/${uuid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function registerCredentialForAccount(name: string, tokenId: string, credential: PublicKeyCredential): Promise<SecurityKey> {
|
async function registerCredentialForAccount(
|
||||||
|
name: string,
|
||||||
|
tokenId: string,
|
||||||
|
credential: PublicKeyCredential,
|
||||||
|
): Promise<SecurityKey> {
|
||||||
const { data } = await http.post('/api/client/account/security-keys/register', {
|
const { data } = await http.post('/api/client/account/security-keys/register', {
|
||||||
name,
|
name,
|
||||||
token_id: tokenId,
|
token_id: tokenId,
|
||||||
|
@ -44,7 +50,9 @@ async function registerCredentialForAccount(name: string, tokenId: string, crede
|
||||||
type: credential.type,
|
type: credential.type,
|
||||||
rawId: encodeBuffer(credential.rawId),
|
rawId: encodeBuffer(credential.rawId),
|
||||||
response: {
|
response: {
|
||||||
attestationObject: encodeBuffer((credential.response as AuthenticatorAttestationResponse).attestationObject),
|
attestationObject: encodeBuffer(
|
||||||
|
(credential.response as AuthenticatorAttestationResponse).attestationObject,
|
||||||
|
),
|
||||||
clientDataJSON: encodeBuffer(credential.response.clientDataJSON),
|
clientDataJSON: encodeBuffer(credential.response.clientDataJSON),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -66,7 +74,9 @@ async function registerSecurityKey(name: string): Promise<SecurityKey> {
|
||||||
|
|
||||||
const credentials = await navigator.credentials.create({ publicKey });
|
const credentials = await navigator.credentials.create({ publicKey });
|
||||||
if (!credentials || credentials.type !== 'public-key') {
|
if (!credentials || credentials.type !== 'public-key') {
|
||||||
throw new Error(`Unexpected type returned by navigator.credentials.create(): expected "public-key", got "${credentials?.type}"`);
|
throw new Error(
|
||||||
|
`Unexpected type returned by navigator.credentials.create(): expected "public-key", got "${credentials?.type}"`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await registerCredentialForAccount(name, data.data.token_id, credentials as PublicKeyCredential);
|
return await registerCredentialForAccount(name, data.data.token_id, credentials as PublicKeyCredential);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import { decodeBase64 } from '@/lib/base64';
|
import { decodeBase64 } from '@/lib/base64';
|
||||||
|
|
||||||
describe('@/lib/base64.ts', function () {
|
describe('@/lib/base64.ts', function () {
|
||||||
|
|
|
@ -13,4 +13,4 @@ function decodeBase64 (input: string): string {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { decodeBase64 }
|
export { decodeBase64 };
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import { decodeBuffer, encodeBuffer } from '@/lib/buffer';
|
import { decodeBuffer, encodeBuffer } from '@/lib/buffer';
|
||||||
|
|
||||||
describe('@/lib/buffer.ts', function () {
|
describe('@/lib/buffer.ts', function () {
|
||||||
|
|
Loading…
Reference in a new issue