From 223caa0ca8ca9f8d796ea388b09e15121248da36 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 15:30:34 +0100 Subject: [PATCH 01/10] opened 4.0-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 570486c..0c6f74f 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.0-dev" } }, "config": { From fc68696f93bf2b5a4b36bb6cf93bafd8093054e9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 30 Oct 2025 23:51:04 +0100 Subject: [PATCH 02/10] requires PHP 8.3 --- .github/workflows/static-analysis.yml | 2 +- .github/workflows/tests.yml | 6 +++--- CLAUDE.md | 4 ++-- composer.json | 2 +- readme.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index b5b0dff..1d8d064 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v6 - uses: shivammathur/setup-php@v2 with: - php-version: 8.2 + php-version: 8.3 coverage: none - run: composer install --no-progress --prefer-dist diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5ee52c1..686f085 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: ['8.1', '8.2', '8.3', '8.4', '8.5'] + php: ['8.3', '8.4', '8.5'] fail-fast: false @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v6 - uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.3 coverage: none - run: composer update --no-progress --prefer-dist --prefer-lowest --prefer-stable @@ -50,7 +50,7 @@ jobs: - uses: actions/checkout@v6 - uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.3 coverage: none - run: composer install --no-progress --prefer-dist diff --git a/CLAUDE.md b/CLAUDE.md index a1da84d..594c396 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Nette Security is a standalone PHP library providing authentication, authorization, and role-based access control (ACL) for the Nette Framework ecosystem. - **Type**: Reusable PHP library/package -- **PHP Support**: 8.1 - 8.5 +- **PHP Support**: 8.3 - 8.5 - **Key Components**: Authentication (User login/logout), Authorization (Permission checking), ACL (Access Control Lists) - **Documentation**: https://doc.nette.org/access-control @@ -574,7 +574,7 @@ public function checkRequirements($element): void ## CI/CD Pipeline GitHub Actions run on all pull requests: -- Tests across PHP 8.1, 8.2, 8.3, 8.4, 8.5 +- Tests across PHP 8.3, 8.4, 8.5 - Code style checks (Nette Code Checker) - Static analysis (PHPStan Level 5) - Code coverage tracking diff --git a/composer.json b/composer.json index 0c6f74f..169ee8a 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "8.1 - 8.5", + "php": "8.3 - 8.5", "nette/utils": "^4.0" }, "require-dev": { diff --git a/readme.md b/readme.md index a9e93b8..9853eab 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,7 @@ Authentication & Authorization library for Nette. Documentation can be found on the [website](https://doc.nette.org/access-control). -It requires PHP version 8.1 and supports PHP up to 8.5. +It requires PHP version 8.3 and supports PHP up to 8.5. [Support Me](https://github.com/sponsors/dg) From a663c1751b861357f971c8a1aa4a65a36691fc7b Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 11 Dec 2023 13:59:38 +0100 Subject: [PATCH 03/10] uses nette/http 4 --- composer.json | 4 ++-- src/Bridges/SecurityHttp/CookieStorage.php | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 169ee8a..84842ec 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ }, "require-dev": { "nette/di": "^3.1", - "nette/http": "^3.2", + "nette/http": "^4.0", "nette/tester": "^2.6", "tracy/tracy": "^2.9", "phpstan/phpstan": "^2.1@stable", @@ -30,7 +30,7 @@ }, "conflict": { "nette/di": "<3.0-stable", - "nette/http": "<3.1.3" + "nette/http": "<4" }, "autoload": { "classmap": ["src/"], diff --git a/src/Bridges/SecurityHttp/CookieStorage.php b/src/Bridges/SecurityHttp/CookieStorage.php index ab9a2d1..03b5a2e 100644 --- a/src/Bridges/SecurityHttp/CookieStorage.php +++ b/src/Bridges/SecurityHttp/CookieStorage.php @@ -45,11 +45,8 @@ public function saveAuthentication(IIdentity $identity): void $this->cookieName, $uid, $this->cookieExpiration, - null, - $this->cookieDomain, - null, - true, - $this->cookieSameSite, + domain: $this->cookieDomain, + sameSite: $this->cookieSameSite, ); } @@ -59,8 +56,7 @@ public function clearAuthentication(bool $clearIdentity): void $this->uid = ''; $this->response->deleteCookie( $this->cookieName, - null, - $this->cookieDomain, + domain: $this->cookieDomain, ); } From 5b594674e78dbde321915bcc2ce6cd5b51110db9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 23 Dec 2025 22:50:45 +0100 Subject: [PATCH 04/10] used PHP 8.2 features --- src/Security/Identity.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Security/Identity.php b/src/Security/Identity.php index 56cc132..cfc5dbb 100644 --- a/src/Security/Identity.php +++ b/src/Security/Identity.php @@ -35,9 +35,7 @@ public function __construct(string|int $id, string|array|null $roles = null, ?it { $this->setId($id); $this->setRoles((array) $roles); - $this->data = $data instanceof \Traversable - ? iterator_to_array($data) - : (array) $data; + $this->data = iterator_to_array($data ?? []); } From 9b3c3268e9498f4e6e70ae78ae0e0626776a5f4e Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 20 Sep 2021 18:39:13 +0200 Subject: [PATCH 05/10] removed deprecated IAuthenticator (BC break) --- src/Bridges/SecurityDI/SecurityExtension.php | 2 +- src/Security/Authenticator.php | 2 +- src/Security/IAuthenticator.php | 35 -------------------- src/Security/User.php | 14 ++++---- 4 files changed, 8 insertions(+), 45 deletions(-) delete mode 100644 src/Security/IAuthenticator.php diff --git a/src/Bridges/SecurityDI/SecurityExtension.php b/src/Bridges/SecurityDI/SecurityExtension.php index 3719288..8cbcebd 100644 --- a/src/Bridges/SecurityDI/SecurityExtension.php +++ b/src/Bridges/SecurityDI/SecurityExtension.php @@ -112,7 +112,7 @@ public function loadConfiguration(): void } $builder->addDefinition($this->prefix('authenticator')) - ->setType(Nette\Security\IAuthenticator::class) + ->setType(Nette\Security\Authenticator::class) ->setFactory(Nette\Security\SimpleAuthenticator::class, [$usersList, $usersRoles, $usersData]); if ($this->name === 'security') { diff --git a/src/Security/Authenticator.php b/src/Security/Authenticator.php index 2b47c70..42c0cab 100644 --- a/src/Security/Authenticator.php +++ b/src/Security/Authenticator.php @@ -11,7 +11,7 @@ /** * Performs authentication. */ -interface Authenticator extends IAuthenticator +interface Authenticator { /** Exception error code */ public const diff --git a/src/Security/IAuthenticator.php b/src/Security/IAuthenticator.php deleted file mode 100644 index 443f37b..0000000 --- a/src/Security/IAuthenticator.php +++ /dev/null @@ -1,35 +0,0 @@ - $roles * @property-read ?int $logoutReason - * @property IAuthenticator $authenticator + * @property Authenticator $authenticator * @property Authorizator $authorizator */ class User @@ -68,7 +68,7 @@ class User public function __construct( private readonly UserStorage $storage, - private ?IAuthenticator $authenticator = null, + private ?Authenticator $authenticator = null, private ?Authorizator $authorizator = null, ) { } @@ -99,9 +99,7 @@ public function login( $this->identity = $username; } else { $authenticator = $this->getAuthenticator(); - $this->identity = $authenticator instanceof Authenticator - ? $authenticator->authenticate(...func_get_args()) - : $authenticator->authenticate(func_get_args()); + $this->identity = $authenticator->authenticate(...func_get_args()); } $id = $this->authenticator instanceof IdentityHandler @@ -214,7 +212,7 @@ final public function refreshStorage(): void /** * Sets authentication handler. */ - public function setAuthenticator(IAuthenticator $handler): static + public function setAuthenticator(Authenticator $handler): static { $this->authenticator = $handler; $this->guestIdentityResolved = false; @@ -225,7 +223,7 @@ public function setAuthenticator(IAuthenticator $handler): static /** * Returns authentication handler. */ - final public function getAuthenticator(): IAuthenticator + final public function getAuthenticator(): Authenticator { if (!$this->authenticator) { throw new Nette\InvalidStateException('Authenticator has not been set.'); @@ -238,7 +236,7 @@ final public function getAuthenticator(): IAuthenticator /** * Returns authentication handler, or null if none is set. */ - final public function getAuthenticatorIfExists(): ?IAuthenticator + final public function getAuthenticatorIfExists(): ?Authenticator { return $this->authenticator; } From 0fe0f5f16365b00a961c012f385ff2d91bd7988a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 12 Dec 2021 18:12:59 +0100 Subject: [PATCH 06/10] IIdentity: added return typehint (BC break) --- src/Security/IIdentity.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Security/IIdentity.php b/src/Security/IIdentity.php index 774a8fa..1b820f9 100644 --- a/src/Security/IIdentity.php +++ b/src/Security/IIdentity.php @@ -16,9 +16,8 @@ interface IIdentity { /** * Returns the ID of user. - * @return string|int */ - function getId(); + function getId(): string|int; /** * Returns a list of roles that the user is a member of. From 78c930aaa000bdd24bb3ad86be6156723b44be3c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 15 Jun 2026 07:42:52 +0200 Subject: [PATCH 07/10] Identity -> SimpleIdentity Identity class must exist as an alias in order to read it from session --- src/Security/Identity.php | 125 -------------------------------- src/Security/SimpleIdentity.php | 111 +++++++++++++++++++++++++++- src/compatibility-intf.php | 35 +++++++++ src/compatibility.php | 26 +------ tests/Security/Identity.phpt | 34 --------- 5 files changed, 149 insertions(+), 182 deletions(-) delete mode 100644 src/Security/Identity.php create mode 100644 src/compatibility-intf.php delete mode 100644 tests/Security/Identity.phpt diff --git a/src/Security/Identity.php b/src/Security/Identity.php deleted file mode 100644 index cfc5dbb..0000000 --- a/src/Security/Identity.php +++ /dev/null @@ -1,125 +0,0 @@ - $roles - * @property array $data - */ -class Identity implements IIdentity -{ - private string|int $id; - - /** @var list */ - private array $roles; - - /** @var array */ - private array $data; - - - /** - * @param string|list|null $roles - * @param ?iterable $data - */ - public function __construct(string|int $id, string|array|null $roles = null, ?iterable $data = null) - { - $this->setId($id); - $this->setRoles((array) $roles); - $this->data = iterator_to_array($data ?? []); - } - - - /** - * Sets the ID of user. - */ - public function setId(string|int $id): static - { - $this->id = is_string($id) && ctype_digit($id) && (string) ($tmp = (int) $id) === $id ? $tmp : $id; - return $this; - } - - - /** - * Returns the ID of user. - */ - public function getId(): string|int - { - return $this->id; - } - - - /** - * Sets a list of roles that the user is a member of. - * @param list $roles - */ - public function setRoles(array $roles): static - { - $this->roles = $roles; - return $this; - } - - - /** - * Returns a list of roles that the user is a member of. - * @return list - */ - public function getRoles(): array - { - return $this->roles; - } - - - /** - * Returns user data. - * @return array - */ - public function getData(): array - { - return $this->data; - } - - - /** - * Sets user data value. - */ - public function __set(string $key, mixed $value): void - { - match ($key) { - 'id' => $this->setId($value), - 'roles' => $this->setRoles($value), - 'data' => $this->data = $value, - default => $this->data[$key] = $value, - }; - } - - - /** - * Returns user data value. - */ - public function &__get(string $key): mixed - { - if (in_array($key, ['id', 'roles', 'data'], strict: true)) { - $res = $this->{'get' . ucfirst($key)}(); - return $res; - - } else { - return $this->data[$key]; - } - } - - - public function __isset(string $key): bool - { - return isset($this->data[$key]) || in_array($key, ['id', 'roles', 'data'], strict: true); - } -} diff --git a/src/Security/SimpleIdentity.php b/src/Security/SimpleIdentity.php index c987f6f..b831a28 100644 --- a/src/Security/SimpleIdentity.php +++ b/src/Security/SimpleIdentity.php @@ -7,10 +7,119 @@ namespace Nette\Security; +use function ctype_digit, in_array, is_string, iterator_to_array; + /** * Default implementation of IIdentity. + * @property string|int $id + * @property list $roles + * @property array $data */ -class SimpleIdentity extends Identity +class SimpleIdentity implements IIdentity { + private string|int $id; + + /** @var list */ + private array $roles; + + /** @var array */ + private array $data; + + + /** + * @param string|list|null $roles + * @param ?iterable $data + */ + public function __construct(string|int $id, string|array|null $roles = null, ?iterable $data = null) + { + $this->setId($id); + $this->setRoles((array) $roles); + $this->data = iterator_to_array($data ?? []); + } + + + /** + * Sets the ID of user. + */ + public function setId(string|int $id): static + { + $this->id = is_string($id) && ctype_digit($id) && (string) ($tmp = (int) $id) === $id ? $tmp : $id; + return $this; + } + + + /** + * Returns the ID of user. + */ + public function getId(): string|int + { + return $this->id; + } + + + /** + * Sets a list of roles that the user is a member of. + * @param list $roles + */ + public function setRoles(array $roles): static + { + $this->roles = $roles; + return $this; + } + + + /** + * Returns a list of roles that the user is a member of. + * @return list + */ + public function getRoles(): array + { + return $this->roles; + } + + + /** + * Returns user data. + * @return array + */ + public function getData(): array + { + return $this->data; + } + + + /** + * Sets user data value. + */ + public function __set(string $key, mixed $value): void + { + match ($key) { + 'id' => $this->setId($value), + 'roles' => $this->setRoles($value), + 'data' => $this->data = $value, + default => $this->data[$key] = $value, + }; + } + + + /** + * Returns user data value. + */ + public function &__get(string $key): mixed + { + if (in_array($key, ['id', 'roles', 'data'], strict: true)) { + $res = $this->{'get' . ucfirst($key)}(); + return $res; + + } else { + return $this->data[$key]; + } + } + + + public function __isset(string $key): bool + { + return isset($this->data[$key]) || in_array($key, ['id', 'roles', 'data'], strict: true); + } } diff --git a/src/compatibility-intf.php b/src/compatibility-intf.php new file mode 100644 index 0000000..fc11a81 --- /dev/null +++ b/src/compatibility-intf.php @@ -0,0 +1,35 @@ + 'John']); - - Assert::same(12, $id->getId()); - Assert::same(12, $id->id); - Assert::same(['admin'], $id->getRoles()); - Assert::same(['admin'], $id->roles); - Assert::same(['name' => 'John'], $id->getData()); - Assert::same(['name' => 'John'], $id->data); - Assert::same('John', $id->name); -}); - - -test('', function () { - $id = new Identity('12'); - Assert::same(12, $id->getId()); - - - $id = new Identity('12345678901234567890'); - Assert::same('12345678901234567890', $id->getId()); -}); From 2407d2521e728eb3d20a50afee12f2c6d12b43e5 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 20 Sep 2021 18:29:35 +0200 Subject: [PATCH 08/10] SimpleIdentity: uses __serialize & __unserialize --- src/Security/SimpleIdentity.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Security/SimpleIdentity.php b/src/Security/SimpleIdentity.php index b831a28..ac749cc 100644 --- a/src/Security/SimpleIdentity.php +++ b/src/Security/SimpleIdentity.php @@ -122,4 +122,22 @@ public function __isset(string $key): bool { return isset($this->data[$key]) || in_array($key, ['id', 'roles', 'data'], strict: true); } + + + public function __serialize(): array + { + return [ + 'id' => $this->id, + 'roles' => $this->roles, + 'data' => $this->data, + ]; + } + + + public function __unserialize(array $data): void + { + $this->id = $data['id'] ?? $data["\00Nette\\Security\\Identity\00id"] ?? 0; + $this->roles = $data['roles'] ?? $data["\00Nette\\Security\\Identity\00roles"] ?? []; + $this->data = $data['data'] ?? $data["\00Nette\\Security\\Identity\00data"] ?? []; + } } From 29ed1fd1cb4c62f0a6de31076d1c53f61fd83c66 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 24 Sep 2021 14:12:52 +0200 Subject: [PATCH 09/10] User: deprecated magic properties (BC break) --- src/Security/User.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Security/User.php b/src/Security/User.php index f9055d2..21387ac 100644 --- a/src/Security/User.php +++ b/src/Security/User.php @@ -17,11 +17,11 @@ * * @property-read bool $loggedIn * @property-read ?IIdentity $identity - * @property-read string|int|null $id - * @property-read list $roles - * @property-read ?int $logoutReason - * @property Authenticator $authenticator - * @property Authorizator $authorizator + * @property-deprecated string|int|null $id + * @property-deprecated list $roles + * @property-deprecated ?int $logoutReason + * @property-deprecated Authenticator $authenticator + * @property-deprecated Authorizator $authorizator */ class User { From b3dcf9fe8a7052efee78ebb8a718386eae9eb709 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 23 May 2026 04:00:44 +0200 Subject: [PATCH 10/10] IdentityHandler: make getGuestIdentity() a regular interface method (BC break) Promotes the optional @method to a required method, so User no longer needs method_exists() to call it. BC break: existing IdentityHandler implementations must now declare getGuestIdentity(). --- src/Security/IdentityHandler.php | 6 +++++- src/Security/User.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Security/IdentityHandler.php b/src/Security/IdentityHandler.php index ed9956f..0252386 100644 --- a/src/Security/IdentityHandler.php +++ b/src/Security/IdentityHandler.php @@ -10,7 +10,6 @@ /** * Serializes and restores identity to/from persistent storage. - * @method ?IIdentity getGuestIdentity() */ interface IdentityHandler { @@ -23,4 +22,9 @@ function sleepIdentity(IIdentity $identity): IIdentity; * Called after identity is read from storage. Typically refreshes roles or validates the token. Returns null to force logout. */ function wakeupIdentity(IIdentity $identity): ?IIdentity; + + /** + * Returns the identity for an anonymous (not logged-in) user, or null if none. + */ + function getGuestIdentity(): ?IIdentity; } diff --git a/src/Security/User.php b/src/Security/User.php index 21387ac..f9c9742 100644 --- a/src/Security/User.php +++ b/src/Security/User.php @@ -178,7 +178,7 @@ private function resolveGuestIdentity(): ?IIdentity { if (!$this->guestIdentityResolved) { $this->guestIdentityResolved = true; - $this->guestIdentity = $this->authenticator instanceof IdentityHandler && method_exists($this->authenticator, 'getGuestIdentity') + $this->guestIdentity = $this->authenticator instanceof IdentityHandler ? $this->authenticator->getGuestIdentity() : null; }