Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ The format is based on Keep a Changelog.
- Added `Response::back()` and the `back()` helper, which redirect to the referrer and fall back to `base_url()` when the request has none, or when the referrer is not on the application origin (same scheme, host and port), which is compared with `base_url()`: behind a proxy that terminates TLS, or where the `Host` header is not pinned, set `app.base_url`. They take the old input to keep for the next request, read with `old()` as after `redirectWith()`, and an optional status code (`back($data, $code)`) (#540)

### Changed
- **BREAKING:** Converted the constant classes `ReservedKeys`, `AuthKeys`, `Pagination` and the database `Type`, `Key` and `Relation` to PHP enums, and moved the table actions of `Table` into the new `TableAction` enum (#612). An enum case is not its value, so code that needs the text or number reads `->value`, and text that comes from outside is turned into a case with `::from()` or `::tryFrom()`:
- `ReservedKeys`, `AuthKeys`, `Pagination`, `Type`, `Key` and `Relation` are string-backed enums with the same case names and values, so `Relation::BELONGS_TO`, `Type::INT` and `Key::INDEX` are written as before. An application that uses a case as an array key, compares it with text, or passes it to a `string` parameter must add `->value`, for example `$this->keyFields[AuthKeys::USERNAME->value]`. Code that reads the constants with `ReflectionClass::getConstants()` uses `AuthKeys::cases()`.
- These methods take the enum and no longer accept text: `Table::addColumn()`, `Table::modifyColumn()` and `Column::__construct()` take `Type`, `Table::addIndex()` takes `Key` and `Table::setAction()` takes `TableAction`. A text value such as `addColumn('id', 'int')` throws a `TypeError`; write `Type::INT`. A subclass of `Table` or `Column` that overrides these methods must declare the enum types.
- `Table::CREATE`, `Table::ALTER`, `Table::DROP` and `Table::RENAME` are removed: use `TableAction::CREATE`, `TableAction::ALTER`, `TableAction::DROP` and `TableAction::RENAME`.
- The `type` of a model relation must be a `Relation` case. The text `'hasOne'` is rejected with the message "Relation type `hasOne` is not supported"; write `Relation::HAS_ONE`. `ModelException::unsupportedRelationType()` accepts a `Relation` or text.
- `Pagination` keeps the four names (`PAGINATION_CLASS`, `PAGINATION_CLASS_ACTIVE`, `PER_PAGE`, `PAGE`) as a string-backed enum, read with `->value`. The numbers `FIRST_PAGE_NUMBER`, `MINIMUM_PAGE_ITEMS_COUNT` and `EDGE_PADDING` moved to `PaginationLimit`, an enum that holds them as constants, read without `->value`.
- The adapter types, `StatusCode`, `ContentType`, `Env`, `ExitCode` and the `ExceptionMessages` classes stay constant classes.
- The `DemoWeb` and `Toolkit` module templates redirect back with `back()` instead of `redirect(get_referrer() ?? base_url())` and `redirectWith(get_referrer() ?? base_url(), $data)`, so generated code gets the same-origin check. Modules generated before keep their code (#541)
- Twig is a required dependency again: `twig/twig` `^3.27` is in `require` and no longer only suggested, which reverses the 3.0.3 change that made it optional while the framework supported PHP 8.0 (#532). Twig `^3.27` needs PHP 8.1, the minimum since 3.1.0. An application that pins an older Twig must allow `^3.27`. Removed `RendererException::twigNotInstalled()` and the check in `TwigAdapter` that threw it when Twig was missing, and the Twig tests no longer skip (#626)
- The test suite runs on PHPUnit `^10.5` (a development dependency) and `phpunit.xml` uses its schema. It now sets `error_reporting` to `-1`: it was the text `E_ALL & ~E_DEPRECATED`, which PHP reads as `0`, so every PHP warning was hidden in the tests (#622)
Expand Down
9 changes: 4 additions & 5 deletions src/App/Enums/ReservedKeys.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,18 @@
namespace Quantum\App\Enums;

/**
* Class ReservedKeys
* Enum ReservedKeys
* @package Quantum\App
*/
final class ReservedKeys
enum ReservedKeys: string
{
/**
* Internal response key for rendered view
*/
public const RENDERED_VIEW = '_qt_rendered_view';
case RENDERED_VIEW = '_qt_rendered_view';

/**
* Internal session key for previous request
*/
public const PREV_REQUEST = '_qt_prev_request';

case PREV_REQUEST = '_qt_prev_request';
}
30 changes: 15 additions & 15 deletions src/Auth/Adapters/JwtAuthAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,18 @@ public function signin(string $username, string $password): string|array
*/
public function signout(): bool
{
$refreshToken = request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]);
$refreshToken = request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value]);

$user = $this->authService->get($this->keyFields[AuthKeys::REFRESH_TOKEN], $refreshToken);
$user = $this->authService->get($this->keyFields[AuthKeys::REFRESH_TOKEN->value], $refreshToken);

if ($user) {
$this->authService->update(
$this->keyFields[AuthKeys::REFRESH_TOKEN],
$this->keyFields[AuthKeys::REFRESH_TOKEN->value],
$refreshToken,
array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN] => ''])
array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN->value] => ''])
);

request()->deleteHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]);
request()->deleteHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value]);
request()->deleteHeader('Authorization');
response()->delete('tokens');

Expand Down Expand Up @@ -142,8 +142,8 @@ protected function getUpdatedTokens(User $user): array
{

return [
$this->keyFields[AuthKeys::REFRESH_TOKEN] => $this->generateToken(),
$this->keyFields[AuthKeys::ACCESS_TOKEN] => base64_encode($this->jwt->setData($this->getVisibleFields($user))->compose()),
$this->keyFields[AuthKeys::REFRESH_TOKEN->value] => $this->generateToken(),
$this->keyFields[AuthKeys::ACCESS_TOKEN->value] => base64_encode($this->jwt->setData($this->getVisibleFields($user))->compose()),
];
}

Expand All @@ -157,13 +157,13 @@ protected function setUpdatedTokens(User $user): array
$tokens = $this->getUpdatedTokens($user);

$this->authService->update(
$this->keyFields[AuthKeys::USERNAME],
$user->getFieldValue($this->keyFields[AuthKeys::USERNAME]),
array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN] => $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN]]])
$this->keyFields[AuthKeys::USERNAME->value],
$user->getFieldValue($this->keyFields[AuthKeys::USERNAME->value]),
array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN->value] => $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN->value]]])
);

request()->setHeader($this->keyFields[AuthKeys::REFRESH_TOKEN], $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN]]);
request()->setHeader('Authorization', 'Bearer ' . $tokens[$this->keyFields[AuthKeys::ACCESS_TOKEN]]);
request()->setHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value], $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN->value]]);
request()->setHeader('Authorization', 'Bearer ' . $tokens[$this->keyFields[AuthKeys::ACCESS_TOKEN->value]]);
response()->set('tokens', $tokens);

return $tokens;
Expand All @@ -175,8 +175,8 @@ protected function setUpdatedTokens(User $user): array
protected function checkRefreshToken(): ?User
{
return $this->authService->get(
$this->keyFields[AuthKeys::REFRESH_TOKEN],
request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN])
$this->keyFields[AuthKeys::REFRESH_TOKEN->value],
request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value])
);
}

Expand All @@ -203,7 +203,7 @@ private function getUserFromAccessToken(): ?User
*/
private function getUserFromRefreshToken(): ?User
{
if (!request()->hasHeader($this->keyFields[AuthKeys::REFRESH_TOKEN])) {
if (!request()->hasHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value])) {
return null;
}

Expand Down
30 changes: 15 additions & 15 deletions src/Auth/Adapters/SessionAuthAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public function user(): ?User
return (new User())->setData(session()->get(self::AUTH_USER));
}

if (cookie()->has($this->keyFields[AuthKeys::REMEMBER_TOKEN])) {
if (cookie()->has($this->keyFields[AuthKeys::REMEMBER_TOKEN->value])) {
$user = $this->checkRememberToken();

if ($user) {
Expand Down Expand Up @@ -171,15 +171,15 @@ public function verifyOtp(int $otp, string $otpToken): bool
private function checkRememberToken(): false|\Quantum\Auth\User
{
$user = $this->authService->get(
$this->keyFields[AuthKeys::REMEMBER_TOKEN],
cookie()->get($this->keyFields[AuthKeys::REMEMBER_TOKEN])
$this->keyFields[AuthKeys::REMEMBER_TOKEN->value],
cookie()->get($this->keyFields[AuthKeys::REMEMBER_TOKEN->value])
);

if (!$user) {
return false;
}

if ($this->isTwoFactorEnabled() && !empty($user->getFieldValue($this->keyFields[AuthKeys::OTP_TOKEN]))) {
if ($this->isTwoFactorEnabled() && !empty($user->getFieldValue($this->keyFields[AuthKeys::OTP_TOKEN->value]))) {
return false;
}

Expand All @@ -195,15 +195,15 @@ private function setRememberToken(User $user): void
$rememberToken = $this->generateToken();

$this->authService->update(
$this->keyFields[AuthKeys::USERNAME],
$user->getFieldValue($this->keyFields[AuthKeys::USERNAME]),
[$this->keyFields[AuthKeys::REMEMBER_TOKEN] => $rememberToken]
$this->keyFields[AuthKeys::USERNAME->value],
$user->getFieldValue($this->keyFields[AuthKeys::USERNAME->value]),
[$this->keyFields[AuthKeys::REMEMBER_TOKEN->value] => $rememberToken]
);

$rememberLifetime = (int) ($this->config['session']['remember_lifetime'] ?? self::DEFAULT_REMEMBER_LIFETIME);

cookie()->set(
$this->keyFields[AuthKeys::REMEMBER_TOKEN],
$this->keyFields[AuthKeys::REMEMBER_TOKEN->value],
$rememberToken,
$rememberLifetime,
'/',
Expand All @@ -218,21 +218,21 @@ private function setRememberToken(User $user): void
*/
private function removeRememberToken(): void
{
if (cookie()->has($this->keyFields[AuthKeys::REMEMBER_TOKEN])) {
if (cookie()->has($this->keyFields[AuthKeys::REMEMBER_TOKEN->value])) {
$user = $this->authService->get(
$this->keyFields[AuthKeys::REMEMBER_TOKEN],
cookie()->get($this->keyFields[AuthKeys::REMEMBER_TOKEN])
$this->keyFields[AuthKeys::REMEMBER_TOKEN->value],
cookie()->get($this->keyFields[AuthKeys::REMEMBER_TOKEN->value])
);

if ($user) {
$this->authService->update(
$this->keyFields[AuthKeys::REMEMBER_TOKEN],
$user->getFieldValue($this->keyFields[AuthKeys::REMEMBER_TOKEN]),
[$this->keyFields[AuthKeys::REMEMBER_TOKEN] => '']
$this->keyFields[AuthKeys::REMEMBER_TOKEN->value],
$user->getFieldValue($this->keyFields[AuthKeys::REMEMBER_TOKEN->value]),
[$this->keyFields[AuthKeys::REMEMBER_TOKEN->value] => '']
);
}

cookie()->delete($this->keyFields[AuthKeys::REMEMBER_TOKEN]);
cookie()->delete($this->keyFields[AuthKeys::REMEMBER_TOKEN->value]);
}
}
}
28 changes: 12 additions & 16 deletions src/Auth/Enums/AuthKeys.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,63 +11,59 @@
namespace Quantum\Auth\Enums;

/**
* Class AuthKeys
* Enum AuthKeys
* @package Quantum\Auth
* @codeCoverageIgnore
*/
final class AuthKeys
enum AuthKeys: string
{
/**
* One time password key
*/
public const OTP = 'otp';
case OTP = 'otp';

/**
* One time password expiry key
*/
public const OTP_EXPIRY = 'otpExpiry';
case OTP_EXPIRY = 'otpExpiry';

/**
* One time password token key
*/
public const OTP_TOKEN = 'otpToken';
case OTP_TOKEN = 'otpToken';

/**
* Username key
*/
public const USERNAME = 'username';
case USERNAME = 'username';

/**
* Password key
*/
public const PASSWORD = 'password';
case PASSWORD = 'password';

/**
* Access token key
*/
public const ACCESS_TOKEN = 'accessToken';
case ACCESS_TOKEN = 'accessToken';

/**
* Refresh token key
*/
public const REFRESH_TOKEN = 'refreshToken';
case REFRESH_TOKEN = 'refreshToken';

/**
* Activation token key
*/
public const ACTIVATION_TOKEN = 'activationToken';
case ACTIVATION_TOKEN = 'activationToken';

/**
* Reset token key
*/
public const RESET_TOKEN = 'resetToken';
case RESET_TOKEN = 'resetToken';

/**
* Remember token key
*/
public const REMEMBER_TOKEN = 'rememberToken';

private function __construct()
{
}
case REMEMBER_TOKEN = 'rememberToken';
}
Loading
Loading